Bagaimana cara menghapus semua kecuali 10 file terbaru di Linux?
49
Saya mencoba untuk menjaga direktori penuh dengan file log yang dapat dikelola. Nightly, saya ingin menghapus semua kecuali 10 yang terbaru. Bagaimana saya bisa melakukan ini dalam satu perintah?
@ Michael Bagaimana saya bisa menjadi duplikat dari yang itu, jika diskusi saya dibuat terlebih dahulu?
dev4life
@ovaherenow "milikmu"? Saya tidak melihat nama Anda di kedua pertanyaan, jadi saya tidak yakin yang Anda maksud. Komentar saya bahkan tidak menunjukkan duplikat yang lain. Tapi itu tidak masalah - itu hanya komentar, dengan tautan. Itu dapat ditambahkan ke salah satu pertanyaan, atau kedua pertanyaan. Terserah orang lain - seorang admin - untuk menandai satu atau yang lain sebagai duplikat. Tidak ada sulap jahat yang dimaksudkan dan tidak boleh ada yang dirasakan. Lebih penting dari pertanyaan mana yang pertama, adalah pertanyaan mana yang memiliki jawaban terbaik - sekali lagi, tautan memfasilitasi diskusi ini, tidak menentukan jawabannya.
michael
@ Michael wow. Itu sangat aneh. Utas ini dibuka oleh saya tapi itu jelas bukan nama pengguna saya. Tapi saya mendapat pemberitahuan tentang utas ini.
dev4life
Jawaban:
58
Untuk solusi portabel dan andal, coba ini:
ls -1tr | head -n -10 | xargs -d '\n' rm -f --
The tail -n -10sintaks di salah satu jawaban yang lain tampaknya tidak bekerja di mana-mana (yaitu, bukan pada sistem RHEL5 saya).
Dan menggunakan $()atau ``pada baris perintah rmmenjalankan risiko
memisahkan nama file dengan spasi putih, dan
melebihi batas karakter baris perintah maksimum.
xargsmemperbaiki kedua masalah ini karena secara otomatis akan mencari tahu berapa banyak argumen yang dapat dilewati dalam batas karakter, dan dengan -d '\n'itu hanya akan terpecah pada batas garis input. Secara teknis ini masih dapat menyebabkan masalah untuk nama file dengan baris baru di dalamnya, tetapi itu jauh lebih umum daripada nama file dengan spasi, dan satu-satunya cara di sekitar baris baru akan jauh lebih rumit, mungkin melibatkan setidaknya awk, jika tidak perl.
Jika Anda tidak memiliki xargs (sistem AIX lama, mungkin?) Anda dapat membuatnya menjadi loop:
ls -1tr | head -n -10 | while IFS= read -r f; do
rm -f "$f"
done
Ini akan sedikit lebih lambat karena memunculkan terpisah rmuntuk setiap file, tetapi masih akan menghindari peringatan 1 dan 2 di atas (tetapi masih menderita baris baru dalam nama file).
@ HaukeLaging: Dari head(1)halaman manual:-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
Isaac Freeman
Pada Solaris 10 headdan xargsberikan kesalahan. Opsi yang benar adalah head -n 10dan xargs rm -f. Perintah penuh adalahls -1tr ./ | head -n 10 | xargs rm -rf
DmitrySandalov
1
Perhatikan ls -1trapakah nomor SATU bukan huruf "L".
pengguna linux shonky
1
Juga harus dicatat bahwa baris baru jarang tetapi karakter yang valid dalam nama file ext. Ini berarti bahwa jika Anda memiliki file "itu", dan "itu \ nthing", jika skrip ini hits "itu \ nthing", itu akan menghapus "itu", kesalahan ketika itu tidak bisa menghapus "benda", dan meninggalkan "that \ nthing" (target yang dimaksud) tidak terpengaruh.
Synthead
1
@IsaacFreeman Saya telah menghapus saran buruk saya, tepuk tangan.
Walf
20
Kode yang ingin Anda sertakan dalam skrip Anda adalah
rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)
Opsi -1(numerik) mencetak setiap file pada satu baris, agar aman. The -fpilihan untuk rmmengatakan itu untuk mengabaikan file tidak ada ketika lskembali apa-apa.
Poster asli, di sini. Saya mencoba perintah ini, tetapi tidak berhasil - saya mendapatkan kesalahan "rm: missing operand"
user75814
2
Apakah Anda menggunakan jalur yang benar? Jelas /path/to/your/logshanya dibuat untuk Anda memasuki jalan yang benar. Untuk menemukan bug mengeksekusi bagian ls -t /path/...dan ls -t /path/... | tail -n +11sendirian dan memeriksa apakah hasilnya benar.
Daniel Böhmer
1
@ HaukeLaging Saya yakin Anda salah. Pikirkan +sebelum nomornya. Itu membuat tailtidak mencetak elemen n terakhir tetapi semua elemen dimulai dari tanggal n. Saya check in lagi dan perintahnya pasti menghapus hanya elemen yang lebih tua dari 10 file terbaru dalam semua keadaan.
Daniel Böhmer
@halo Memang, maaf.
Hauke Laging
2
Saya pikir jawaban Anda sangat berbahaya lsmencetak nama file, bukan jalan sehingga Anda rm -fakan mencoba untuk menghapus file dalam direktori saat ini
-mtime 10 -deletehanya menghapus file yang dimodifikasi persis 10 hari yang lalu. File yang lebih lama tidak akan dihapus. -mtime +11 -deleteakan menghapus file yang telah dimodifikasi 11 hari atau lebih yang lalu meninggalkan 10 hari terakhir file log.
Markus
1
Saya pikir penggunaan %pbukannya %Pdiperlukan pada printfagar file mendapat jalur penuh. Kalau rm -rftidak, tidak akan berhasil.
jalopaba
9
Alat seperti logrotate melakukan ini untuk Anda. Itu membuat manajemen log jauh lebih mudah. Anda juga dapat menyertakan rutin pembersihan tambahan seperti halo yang disarankan.
#! /bin/sh
# keepnewest
#
# Simple directory trimming tool to handle housekeeping
# Scans a directory and deletes all but the N newest files
#
# Usage: cleanup <dir> <number of files to keep>
#
# v 1.0 Piers Goodhew 1/mar/2007. No rights retained.
if [ $# -ne 2 ]; then
echo 1>&2 "Usage: $0 <dir> <number of files to keep>"
exit 1
fi
cd $1
files_in_dir=`ls | wc -l`
files_to_delete=`expr $files_in_dir - $2`
if [ $files_to_delete -gt 0 ]; then
ls -t | tail -n $files_to_delete | xargs rm
if [ $? -ne 0 ]; then
echo "An error ocurred deleting the files"
exit 1
else
echo "$files_to_delete file(s) deleted."
fi
else
echo "nothing to delete!"
fi
Terima kasih telah memberikan jawaban ini. Meskipun memberikan kode seperti ini sangat membantu, tetapi juga membantu memberikan beberapa informasi tambahan tentang bagaimana kode itu dapat digunakan atau apa yang dilakukan oleh kode tersebut. Anda dapat menggunakan tombol edit untuk melakukan perbaikan pada posting Anda di masa mendatang.
nhinkle
1
Di Bash Anda dapat mencoba:
ls -1t | (i=0; while read f; do
if [ $i -lt 10 ]; then
((i++))
continue
else
rm -f "$f"
fi
done)
Ini melompati 10 juga yang terbaru menghapus sisanya. logrotate mungkin lebih baik, saya hanya ingin memperbaiki jawaban terkait shell yang salah.
tidak yakin ini akan membantu siapa pun selain untuk menghindari potensi masalah dengan karakter miring saya hanya digunakan lsuntuk melakukan penyortiran tetapi kemudian menggunakan file inodeuntuk menghapus.
Untuk direktori saat ini, ini menyimpan 10 file terbaru berdasarkan waktu modifikasi.
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 terbalik.
Kesalahan dengan spasi dan ketika tidak ada file yang akan dihapus keduanya diselesaikan dengan cara standar:
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
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. Ketika kita membaca semua waktu yang tidak dapat dilakukan, saya mohon berbeda: di sini adalah metode.
Contoh untuk file tar. Tanpa masalah tentang spasi dalam nama file. Untuk menguji, ganti "rm" dengan "ls".
Dalam hal menggunakan ls -tperintah tidak akan membahayakan contoh konyol seperti: touch 'foo " bar'dan touch 'hello * world'. Bukan berarti kami pernah membuat file dengan nama seperti itu di kehidupan nyata!
Sidenote. Jika kita ingin melewatkan variabel ke sh dengan cara ini, kita cukup memodifikasi cetakan:
print "VarName="$1
untuk mengatur variabel VarNameke nilai $1. Beberapa variabel dapat dibuat dalam sekali jalan. Ini VarNamemenjadi variabel sh normal dan biasanya dapat digunakan dalam skrip atau shell sesudahnya. Jadi, untuk membuat variabel dengan awk dan mengembalikannya ke shell:
Jawaban:
Untuk solusi portabel dan andal, coba ini:
The
tail -n -10
sintaks di salah satu jawaban yang lain tampaknya tidak bekerja di mana-mana (yaitu, bukan pada sistem RHEL5 saya).Dan menggunakan
$()
atau``
pada baris perintahrm
menjalankan risikoxargs
memperbaiki kedua masalah ini karena secara otomatis akan mencari tahu berapa banyak argumen yang dapat dilewati dalam batas karakter, dan dengan-d '\n'
itu hanya akan terpecah pada batas garis input. Secara teknis ini masih dapat menyebabkan masalah untuk nama file dengan baris baru di dalamnya, tetapi itu jauh lebih umum daripada nama file dengan spasi, dan satu-satunya cara di sekitar baris baru akan jauh lebih rumit, mungkin melibatkan setidaknya awk, jika tidak perl.Jika Anda tidak memiliki xargs (sistem AIX lama, mungkin?) Anda dapat membuatnya menjadi loop:
Ini akan sedikit lebih lambat karena memunculkan terpisah
rm
untuk setiap file, tetapi masih akan menghindari peringatan 1 dan 2 di atas (tetapi masih menderita baris baru dalam nama file).sumber
head(1)
halaman manual:-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
head
danxargs
berikan kesalahan. Opsi yang benar adalahhead -n 10
danxargs rm -f
. Perintah penuh adalahls -1tr ./ | head -n 10 | xargs rm -rf
ls -1tr
apakah nomor SATU bukan huruf "L".Kode yang ingin Anda sertakan dalam skrip Anda adalah
Opsi
-1
(numerik) mencetak setiap file pada satu baris, agar aman. The-f
pilihan untukrm
mengatakan itu untuk mengabaikan file tidak ada ketikals
kembali apa-apa.sumber
/path/to/your/logs
hanya dibuat untuk Anda memasuki jalan yang benar. Untuk menemukan bug mengeksekusi bagianls -t /path/...
danls -t /path/... | tail -n +11
sendirian dan memeriksa apakah hasilnya benar.+
sebelum nomornya. Itu membuattail
tidak mencetak elemen n terakhir tetapi semua elemen dimulai dari tanggal n. Saya check in lagi dan perintahnya pasti menghapus hanya elemen yang lebih tua dari 10 file terbaru dalam semua keadaan.ls
mencetak nama file, bukan jalan sehingga Andarm -f
akan mencoba untuk menghapus file dalam direktori saat iniTernyata penguraian
ls
itu jahat .Jika setiap file dibuat setiap hari dan Anda ingin menyimpan file yang dibuat dalam 10 hari terakhir yang dapat Anda lakukan:
Atau jika setiap file dibuat secara sewenang-wenang:
sumber
-mtime 10 -delete
hanya menghapus file yang dimodifikasi persis 10 hari yang lalu. File yang lebih lama tidak akan dihapus.-mtime +11 -delete
akan menghapus file yang telah dimodifikasi 11 hari atau lebih yang lalu meninggalkan 10 hari terakhir file log.%p
bukannya%P
diperlukan padaprintf
agar file mendapat jalur penuh. Kalaurm -rf
tidak, tidak akan berhasil.Alat seperti logrotate melakukan ini untuk Anda. Itu membuat manajemen log jauh lebih mudah. Anda juga dapat menyertakan rutin pembersihan tambahan seperti halo yang disarankan.
sumber
Saya sedikit memodifikasi pendekatan Isaac .
Sekarang bekerja dengan jalur yang diinginkan:
sumber
Dari sini :
sumber
Di Bash Anda dapat mencoba:
Ini melompati 10 juga yang terbaru menghapus sisanya. logrotate mungkin lebih baik, saya hanya ingin memperbaiki jawaban terkait shell yang salah.
sumber
tidak yakin ini akan membantu siapa pun selain untuk menghindari potensi masalah dengan karakter miring saya hanya digunakan
ls
untuk melakukan penyortiran tetapi kemudian menggunakan fileinode
untuk menghapus.Untuk direktori saat ini, ini menyimpan 10 file terbaru berdasarkan waktu modifikasi.
ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;
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 terbalik.
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. Ketika kita membaca semua waktu yang tidak dapat dilakukan, saya mohon berbeda: di sini adalah metode.
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 melewatkan variabel ke sh dengan cara ini, kita cukup memodifikasi cetakan:
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 sesudahnya. Jadi, untuk membuat variabel dengan awk dan mengembalikannya ke shell:sumber
Saya setuju bahwa parsing itu jahat!
Pastikan hanya 5 file terakhir yang disimpan di
/srv/backups
jalur.sumber
Berdasarkan respons Isaac Freeman, tetapi bekerja pada direktori apa pun:
Anda juga dapat mengatur awalan, seperti
$PATH_TO_DELETE/testFi*
Jika Anda menggunakan
*
setelahPATH
ls
akan menampilkan nama file absolut, setidaknya di "my" bash :)sumber