Masalah dengan jawaban yang ada:
- ketidakmampuan untuk menangani nama file dengan spasi yang disematkan atau baris baru.
- dalam hal solusi yang menjalankan
rm
langsung pada substitusi perintah yang tidak dikutip ( rm `...`
), ada risiko tambahan globbing yang tidak diinginkan.
- ketidakmampuan untuk membedakan antara file dan direktori (yaitu, jika direktori kebetulan berada di antara 5 item sistem file yang paling baru dimodifikasi, Anda akan secara efektif menyimpan kurang dari 5 file, dan mendaftar
rm
ke direktori akan gagal).
Jawaban wnoise menjawab masalah-masalah ini, tetapi solusinya adalah spesifik- GNU (dan cukup rumit).
Berikut ini adalah pragmatis, solusi POSIX-compliant yang datang hanya dengan satu peringatan : tidak dapat menangani nama file dengan baris baru yang disematkan - tapi saya tidak menganggap itu masalah dunia nyata bagi kebanyakan orang.
Sebagai catatan, inilah penjelasan mengapa umumnya bukan ide yang baik untuk mengurai ls
output: http://mywiki.wooledge.org/ParsingLs
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
Di atas tidak efisien , karena xargs
harus memanggil rm
sekali untuk setiap nama file.
Platform Andaxargs
memungkinkan Anda untuk mengatasi masalah ini:
Jika Anda memiliki GNU xargs
, gunakan -d '\n'
, yang xargs
menganggap setiap input baris argumen yang terpisah, namun melewati banyak argumen yang sesuai pada baris perintah sekaligus :
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
-r
( --no-run-if-empty
) memastikan bahwa rm
tidak dipanggil jika tidak ada input.
Jika Anda memiliki BSD xargs
(termasuk di macOS ), Anda dapat menggunakan -0
untuk menangani NUL
input yang dipisahkan, setelah terlebih dahulu menerjemahkan baris baru ke NUL
( 0x0
) karakter., Yang juga meneruskan (biasanya) semua nama file sekaligus (juga akan bekerja dengan GNU xargs
):
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
Penjelasan:
ls -tp
mencetak nama-nama item filesystem yang diurutkan berdasarkan bagaimana mereka baru-baru ini dimodifikasi, dalam urutan menurun (item yang paling baru dimodifikasi terlebih dahulu) ( -t
), dengan direktori dicetak dengan trailing /
untuk menandainya ( -p
).
grep -v '/$'
lalu menyingkirkan direktori dari daftar yang dihasilkan, dengan menghilangkan ( -v
) baris yang memiliki trailing /
( /$
).
- Peringatan : Karena symlink yang menunjuk ke suatu direktori secara teknis bukan direktori itu sendiri, symlink tersebut tidak akan dikecualikan.
tail -n +6
melompati 5 entri pertama dalam daftar, yang berlaku mengembalikan semua kecuali 5 file yang paling baru dimodifikasi, jika ada.
Perhatikan bahwa untuk mengecualikan N
file, N+1
harus diteruskan ke tail -n +
.
xargs -I {} rm -- {}
(dan variasinya) kemudian memanggil rm
semua file ini; jika tidak ada kecocokan sama sekali, xargs
tidak akan melakukan apa pun.
xargs -I {} rm -- {}
mendefinisikan placeholder {}
yang mewakili setiap jalur input secara keseluruhan , sehingga rm
kemudian dipanggil sekali untuk setiap jalur input, tetapi dengan nama file dengan ruang tertanam yang ditangani dengan benar.
--
dalam semua kasus memastikan bahwa setiap nama file yang terjadi untuk memulai dengan -
tidak keliru untuk pilihan oleh rm
.
Sebuah variasi pada masalah asli, jika file yang cocok harus diproses secara individu atau dikumpulkan dalam array shell :
# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done
# One by one, but using a Bash process substitution (<(...),
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)
# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements
ls
tidak berada di direktori saat ini, maka path ke file akan berisi '/', yang berartigrep -v '/'
tidak akan cocok dengan apa pun. Saya percayagrep -v '/$'
Anda hanya ingin mengecualikan direktori.grep
perintah secara konsep lebih jelas. Perhatikan, bagaimanapun, bahwa masalah yang Anda jelaskan tidak akan muncul dengan jalur direktori tunggal; misalnya,ls -p /private/var
masih hanya akan mencetak nama file belaka. Hanya jika Anda melewati beberapa argumen file (biasanya melalui glob) Anda akan melihat path aktual di output; mis.,ls -p /private/var/*
(dan Anda juga akan melihat isi subdirektori yang cocok, kecuali jika Anda juga termasuk-d
).Hapus semua kecuali 5 (atau apa pun nomor) dari file terbaru dalam direktori.
sumber
ls -t
kels -td *.bz2
ls -t | awk 'NR>1'
(Saya hanya menginginkan yang terbaru). Terima kasih!ls -t | awk 'NR>5' | xargs rm -f
jika Anda lebih suka pipa dan Anda perlu menekan kesalahan jika tidak ada yang dihapus.touch 'hello * world'
, ini akan menghapus semua yang ada di direktori saat ini .Versi ini mendukung nama dengan spasi:
sumber
(ls -t|head -n 5;ls)
adalah grup perintah . Ini mencetak 5 file terbaru dua kali.sort
menyatukan garis identik.uniq -u
menghapus duplikat, sehingga semua kecuali 5 file terbaru tetap.xargs rm
memanggilrm
masing-masing dari mereka.--no-run-if-empty
kexargs
sebagai dalam(ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rm
silakan perbarui jawabannya.touch 'foo " bar'
akan membuang seluruh sisa perintah.xargs -d $'\n'
daripada menyuntikkan kutipan ke konten Anda, meskipun NUL-membatasi aliran input (yang mengharuskan menggunakan sesuatu selainls
untuk benar - benar - melakukannya dengan benar) adalah pilihan yang ideal.Varian sederhana dari jawaban thelsdj:
ls -tr menampilkan semua file, terlama lebih dulu (-t terbaru dulu, -r terbalik).
head -n -5 menampilkan semua kecuali 5 baris terakhir (yaitu 5 file terbaru).
xargs rm memanggil rm untuk setiap file yang dipilih.
sumber
-1
default ketika output ke pipeline, jadi tidak wajib di sini. Ini memiliki masalah yang jauh lebih besar, terkait dengan perilaku defaultxargs
saat mem-parsing nama dengan spasi, tanda kutip, & c.--no-run-if-empty
tidak dikenali di shell saya. Saya menggunakan Cmder di windows.-0
opsi jika nama file dapat berisi spasi putih. Belum mengujinya. sumberMemerlukan GNU find untuk -printf, dan GNU sort untuk -z, dan GNU awk untuk "\ 0", dan GNU xargs untuk -0, tetapi menangani file dengan baris atau spasi baru.
sumber
awk
logika. Apakah saya kehilangan beberapa persyaratan di dalam pertanyaan OP yang membuatnya perlu?while read -r -d ' '; IFS= -r -d ''; do ...
loop shell - pembacaan pertama berakhir pada spasi, sedangkan yang kedua berlanjut ke NUL.sed -z -e 's/[^ ]* //; 1,5d'
adalah yang paling jelas. (atau mungkinsed -n -z -e 's/[^ ]* //; 6,$p'
Semua jawaban ini gagal ketika ada direktori di direktori saat ini. Inilah sesuatu yang berfungsi:
Ini:
berfungsi ketika ada direktori di direktori saat ini
mencoba untuk menghapus setiap file walaupun yang sebelumnya tidak dapat dihapus (karena izin, dll.)
gagal aman ketika jumlah file dalam direktori saat ini berlebihan dan
xargs
biasanya akan mengacaukan Anda (file-x
)tidak memenuhi spasi dalam nama file (mungkin Anda menggunakan OS yang salah?)
sumber
find
mengembalikan lebih banyak nama file daripada yang dapat diteruskan pada satu baris perintahls -t
? (Petunjuk: Anda mendapatkan beberapa kali menjalankanls -t
, yang masing-masing hanya diurutkan secara individual, daripada memiliki urutan pengurutan yang benar secara global; dengan demikian, jawaban ini rusak saat menjalankan dengan direktori yang cukup besar).Daftar nama file dengan waktu modifikasi, mengutip setiap nama file. Kecualikan dulu 3 (3 terbaru). Hapus yang tersisa.
Sunting setelah komentar bermanfaat dari mklement0 (terima kasih!): Dikoreksi -n + 3 argumen, dan perhatikan ini tidak akan berfungsi seperti yang diharapkan jika nama file berisi baris baru dan / atau direktori berisi subdirektori.
sumber
-Q
pilihan tampaknya tidak ada di mesin saya.-Q
. Ya,-Q
adalah ekstensi GNU (inilah spesifikasi POSIXls
). Peringatan kecil (jarang masalah dalam praktiknya):-Q
menyandikan baris baru yang disematkan dalam nama file sebagai literal\n
, yangrm
tidak akan dikenali. Untuk mengecualikan 3 yang pertama ,xargs
argumen harus+4
. Akhirnya, peringatan yang berlaku untuk sebagian besar jawaban lain juga: perintah Anda hanya akan berfungsi sebagaimana dimaksud jika tidak ada subdirektori dalam direktori saat ini.--no-run-if-empty
opsi:ls -tQ | tail -n+4 | xargs --no-run-if-empty rm
Mengabaikan baris baru berarti mengabaikan keamanan dan pengkodean yang baik. Wnoise punya satu-satunya jawaban yang bagus. Berikut ini adalah variasi pada miliknya yang menempatkan nama file dalam array $ x
sumber
IFS
- jika tidak, Anda akan berisiko kehilangan spasi putih dari nama file. Dapatkah lingkup itu ke perintah baca:while IFS= read -rd ''; do
"${REPLY#* }"
?Jika nama file tidak memiliki spasi, ini akan berfungsi:
Jika nama file memiliki spasi, kira-kira seperti
Logika dasar:
sumber
while read
trik untuk berurusan dengan spasi:ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
while IFS= read -r d
akan sedikit lebih baik --r
mencegah backslash literals dari dikonsumsi olehread
, danIFS=
mencegah pemangkasan otomatis spasi spasi tambahan.touch $'hello \'$(rm -rf ~)\' world'
; tanda kutip literal di dalam nama file akan melawan tanda kutip literal yang Anda tambahkansed
, menghasilkan kode dalam nama file yang dieksekusi.| sh
formulir, yang merupakan salah satu yang memiliki kerentanan injeksi shell).Dengan zsh
Dengan asumsi Anda tidak peduli dengan direktori yang ada dan Anda tidak akan memiliki lebih dari 999 file (pilih jumlah yang lebih besar jika Anda mau, atau buat loop sementara).
Dalam
*(.om[6,999])
,.
file mean,o
urutan sort up, rata-ratam
berdasarkan tanggal modifikasi (dimasukkana
untuk waktu akses atauc
untuk perubahan inode),[6,999]
memilih rentang file, jadi jangan pilih 5 terlebih dahulu.sumber
om
) untuk bekerja (sorting apa pun yang saya coba tidak menunjukkan efek - baik pada OSX 10.11.2 (mencoba dengan zsh 5.0.8 dan 5.1.1) , atau di Ubuntu 14.04 (zsh 5.0.2)) - apa yang saya lewatkan ?. Adapun kisaran endpoint: tidak perlu keras-kode itu, hanya menggunakan-1
untuk merujuk ke entri terakhir dan dengan demikian mencakup semua file yang tersisa:[6,-1]
.Saya menyadari ini adalah utas lama, tetapi mungkin seseorang akan mendapat manfaat dari ini. Perintah ini akan menemukan file di direktori saat ini:
Ini sedikit lebih kuat daripada beberapa jawaban sebelumnya karena memungkinkan untuk membatasi domain pencarian Anda ke file yang cocok dengan ekspresi. Pertama, temukan file yang cocok dengan kondisi apa pun yang Anda inginkan. Cetak file-file itu dengan cap waktu di sebelahnya.
Selanjutnya, urutkan berdasarkan stempel waktu:
Kemudian, matikan 4 file terbaru dari daftar:
Raih kolom ke-2 (nama file, bukan cap waktu):
Dan kemudian bungkus semuanya menjadi pernyataan:
Ini mungkin perintah yang lebih verbose, tapi saya lebih beruntung bisa menargetkan file kondisional dan menjalankan perintah yang lebih kompleks terhadap mereka.
sumber
menemukan cmd menarik di Sed-Onliners - Hapus 3 baris terakhir - dan itu sempurna untuk cara lain menguliti kucing (oke tidak), tetapi ide:
sumber
Menghapus semua kecuali 10 file terbaru (paling baru)
Jika kurang dari 10 file tidak ada file yang dihapus dan Anda akan memiliki: kepala kesalahan: jumlah baris ilegal - 0
Untuk menghitung file dengan bash
sumber
Saya membutuhkan solusi elegan untuk busybox (router), semua xargs atau solusi array tidak berguna bagi saya - tidak ada perintah seperti itu tersedia di sana. temukan dan mtime bukanlah jawaban yang tepat karena kita berbicara tentang 10 item dan belum tentu 10 hari. Jawaban Espo adalah yang terpendek dan terbersih dan kemungkinan yang paling tidak bertentangan.
Kesalahan dengan spasi dan ketika tidak ada file yang akan dihapus keduanya diselesaikan dengan cara standar:
Versi yang sedikit lebih edukatif: Kita bisa melakukan semuanya jika kita menggunakan awk secara berbeda. Biasanya, saya menggunakan metode ini untuk mengirimkan (mengembalikan) variabel dari awk ke sh. Seperti yang kita baca sepanjang waktu yang tidak bisa dilakukan, saya mohon berbeda: inilah caranya.
Contoh untuk file tar. Tanpa masalah tentang spasi dalam nama file. Untuk menguji, ganti "rm" dengan "ls".
Penjelasan:
ls -td *.tar
daftar semua file tar. Diurutkan berdasarkan waktu. Untuk menerapkan ke semua file di folder saat ini, hapus bagian "d * .tar"awk 'NR>7...
melompati 7 baris pertamaprint "rm \"" $0 "\""
membangun sebuah baris: rm "nama file"eval
jalankan ituKarena kita menggunakan
rm
, saya tidak akan menggunakan perintah di atas dalam skrip! Penggunaan Wiser adalah:Dalam hal menggunakan
ls -t
perintah tidak akan membahayakan contoh konyol seperti:touch 'foo " bar'
dantouch 'hello * world'
. Bukan berarti kami pernah membuat file dengan nama seperti itu di kehidupan nyata!Sidenote. Jika kita ingin meneruskan variabel ke sh dengan cara ini, kita cukup memodifikasi cetakan (bentuk sederhana, tidak ada ruang yang ditoleransi):
untuk mengatur variabel
VarName
ke nilai$1
. Beberapa variabel dapat dibuat dalam sekali jalan. IniVarName
menjadi variabel sh normal dan biasanya dapat digunakan dalam skrip atau shell setelahnya. Jadi, untuk membuat variabel dengan awk dan mengembalikannya ke shell:sumber
sumber
xargs
tanpa-0
atau minimal-d $'\n'
tidak dapat diandalkan; amati bagaimana ini berperilaku dengan file dengan spasi atau karakter kutipan dalam namanya.Saya membuat ini menjadi skrip bash shell. Penggunaan: di
keep NUM DIR
mana NUM adalah jumlah file yang harus disimpan dan DIR adalah direktori untuk digosok.sumber