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?

pengguna75814
sumber
3
lihat logrotate
knittl
@ 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

  1. memisahkan nama file dengan spasi putih, dan
  2. 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).

Isaac Freeman
sumber
@ 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.

Daniel Böhmer
sumber
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
Jürgen Steinblock
14

Ternyata penguraian lsitu jahat .

Jika setiap file dibuat setiap hari dan Anda ingin menyimpan file yang dibuat dalam 10 hari terakhir yang dapat Anda lakukan:

find /path/to/files -mtime 10 -delete

Atau jika setiap file dibuat secara sewenang-wenang:

find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf
Yang satu
sumber
5
-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.

BillThor
sumber
2

Saya sedikit memodifikasi pendekatan Isaac .

Sekarang bekerja dengan jalur yang diinginkan:

ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
Sebastian U.
sumber
1

Dari sini :

#! /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
manoflinux
sumber
1
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.

Hauke ​​Laging
sumber
1

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.

ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;

Flo Woo
sumber
0

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".

eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')

Penjelasan:

ls -td *.tardaftar 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 pertama

print "rm \"" $0 "\"" membangun sebuah baris: rm "nama file"

eval jalankan itu

Karena kita menggunakan rm, saya tidak akan menggunakan perintah di atas dalam skrip! Penggunaan Wiser adalah:

(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))

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:

eval $(ls -td *.tar | awk 'NR>7 { print "VarName="$1 }'); echo "$VarName"
Pila
sumber
0

Saya setuju bahwa parsing itu jahat!

Pastikan hanya 5 file terakhir yang disimpan di /srv/backupsjalur.

find /srv/backups -maxdepth 1 -type f -printf '%Ts\t%P\n' \
    | sort -rn \
    | tail -n +6 \
    | cut -f2- \
    | xargs -r r
h0tw1r3
sumber
0

Berdasarkan respons Isaac Freeman, tetapi bekerja pada direktori apa pun:

ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --

Anda juga dapat mengatur awalan, seperti $PATH_TO_DELETE/testFi*

Jika Anda menggunakan *setelah PATH lsakan menampilkan nama file absolut, setidaknya di "my" bash :)

BiS
sumber