Cara cepat untuk menghapus file dengan kurang dari x baris

10

Apa cara cepat dan tidak terlalu rumit untuk menghapus semua file dalam direktori yang panjangnya di bawah x baris, dalam bash?

durrrutti
sumber

Jawaban:

10

Berikut adalah solusi POSIX yang harus cukup mudah dipahami:

find . -type f -exec awk -v x=10 'NR==x{exit 1}' {} \; -exec echo rm -f {} \;

Seperti dalam jawaban Stephane , lepaskan echoketika senang dengan apa yang akan dihapus.


Penjelasan, ditulis untuk mereka yang benar-benar baru di Unix / Linux:

Titik .mewakili direktori saat ini. findmenemukan file dan direktori secara rekursif di dalam ., dan dapat melakukan sesuatu dengan mereka.

-typeadalah salah satu find's primary ; ini adalah tes yang akan dilakukan untuk setiap file dan direktori yang secara rekursif ditemukan (di dalam .), dan sisa dari pendahuluan pada baris hanya dievaluasi jika ini menghasilkan "benar."

Dalam kasus khusus ini, kami hanya melanjutkan jika kami berurusan dengan file biasa , bukan direktori atau sesuatu yang lain (misalnya perangkat blok.)


The -executama (dari find) memanggil perintah eksternal, dan hanya hasil untuk utama berikutnya jika keluar perintah eksternal berhasil (status keluar dari "0"). The {}diganti dengan nama file yang "dianggap" oleh findperintah. Jadi -execpanggilan pertama setara dengan perintah shell berikut, dieksekusi untuk setiap file secara bergantian:

awk -v x=10 'NR==x{exit 1}' ./somefilename

Awk adalah seluruh bahasa itu sendiri, yang dirancang untuk menangani file teks terbatas seperti CSV. Persyaratan dan perintah Awk (yang terkandung di antara tanda kutip tunggal dan mulai dengan huruf NR) dieksekusi untuk setiap baris file teks. (Perulangan implisit.)

Untuk mempelajari Awk sepenuhnya, saya sangat merekomendasikan Tutorial Grymoire , tetapi saya akan menjelaskan fitur Awk yang digunakan dalam perintah di atas.


The -vbendera untuk AWK memungkinkan kita untuk mengatur variabel AWK (sekali) sebelum perintah AWK dieksekusi (untuk setiap baris dari file.) Dalam hal ini kita set xke 10.


NRadalah variabel AWK khusus mengacu pada " N Banyaknya arus R ecord." Dengan kata lain, itu adalah nomor baris yang kita lihat pada setiap melewati tertentu melalui loop.

(Perhatikan bahwa adalah mungkin, meskipun tidak biasa, menggunakan yang berbeda " R ecord S eparator" dari default dari karakter baris baru, oleh pengaturan RS. Berikut adalah contoh dari bermain dengan pemisah record. )


Skrip awk secara umum terdiri dari kondisi (kurung kurawal luar) yang digabungkan dengan aksi (kurung kurawal luar.) Bisa ada kondisi majemuk dan aksi majemuk, dan ada kondisi default (benar) dan tindakan default (cetak), tetapi kita perlu tidak peduli dengan itu.

The Kondisi di sini adalah, "Apakah ini garis-10?" Jika demikian, kami keluar dengan status keluar bukan nol, yang dalam skrip shell berarti "penghentian perintah yang gagal."

Dengan demikian satu-satunya cara perintah Awk ini akan keluar dengan sukses adalah jika akhir file tercapai sebelum baris ke-10 tercapai.

Jadi, jika skrip Awk berhasil keluar, itu berarti Anda memiliki file kurang dari sepuluh baris.


-execPanggilan berikutnya (jika Anda menghapus echo) akan menghapus setiap file (yang sejauh itu dalam penilaian findpendahuluan) dengan menjalankan:

rm -f ./somefilename
Wildcard
sumber
5

Dengan asumsi findimplementasi yang mendukung -readablepredikat (jika Anda findtidak mendukungnya, hapus saja, Anda hanya akan mendapatkan pesan kesalahan untuk file yang tidak dapat dibaca, atau diganti dengan -exec test -r {} \;):

x=10 find . -type f -readable -exec sh -c '
  for file do
    lines=$(wc -l < "$file") && [ "$((lines))" -lt "$x" ] && echo rm -f "$file"
  done' sh {} +

Hapus echojika senang.

Itu tidak terlalu efisien dalam hal itu penting semua baris dalam setiap file sementara hanya perlu berhenti di xth satu dan berjalan satu wc(dan berpotensi satu rm) perintah untuk setiap file.

Dengan GNU awk, Anda dapat membuatnya jauh lebih efisien dengan:

x=10
find . -type f -readable -exec awk -v x="$x" -v ORS='\0' '
  FNR == x {nextfile}
  ENDFILE {if (FNR < x) print FILENAME}' {} +|
  xargs -r0 echo rm -f

(lagi, hapus echosaat bahagia).

Sama dengan perl:

x=10 find . -type f -readable -exec perl -Tlne '
  if ($. == $ENV{x}) {close ARGV}
  elsif (eof) {print $ARGV; close ARGV}' {} +

Ganti printdengan unlinkjika senang.

Stéphane Chazelas
sumber
1. Untuk apa terakhir sh? 2. Apakah wc -l < "$file"lebih cepat daripada wc -l "$file"? 3. Bagaimana dia tahu nilai $x, yang didefinisikan dalam Bash shell panggilan?
3
@ Thomas, yang terakhir shadalah apa yang ada di skrip inline itu $0, untuk digunakan untuk pesan kesalahan misalnya. wc -l "$file"akan mencetak nama file yang tidak kita inginkan di sini dan akan berjalan wcbahkan jika file tidak dapat dibuka. $xdiekspor ke find( x=10 find...) yang dengan sendirinya meneruskannya ke sh.
Stéphane Chazelas
Terima kasih! Tapi saya kira kesalahan ini yang saya dapatkan di OSX berarti bahwa versi Bash saya tidak mendukung tanda -baca? find: -readable: unknown primary or operator.
durrrutti
1
@ Durrrutti, itu tidak sampai bash. bashhanyalah penerjemah baris perintah, tetapi findimplementasinya. -readableadalah ekstensi GNU, tidak tersedia di OS / X find. Ini hanya digunakan untuk membatasi ke file yang dapat dibaca (Anda tidak akan bisa mendapatkan jumlah baris untuk file yang tidak dapat dibaca). Anda dapat menghilangkannya untuk yang pertama, kemudian Anda hanya akan mendapatkan pesan kesalahan saat membuka file untuk wcfile yang tidak dapat dibaca.
Stéphane Chazelas
@ StéphaneChazelas, jawaban ini sangat sulit. Saya bertanya-tanya: Apakah saya ketinggalan kasus tepi dengan jawaban saya? :)
Wildcard
2

Demi kelengkapan, selain AWK Anda juga dapat menggunakan GNU sed untuk mencapai hasil yang sama:

find . -type f -exec sed 11q1 '{}' ';' -exec echo rm -f '{}' ';'

Yang menghasilkan baris perintah yang lebih ringkas.

Penjelasan

11 - is the address, i.e. "the eleventh line"
q - is for _q_uit (abort the execution)
1 - is the exit code parameter for q (GNU sed extension) 
zeppelin
sumber