Apa cara cepat dan tidak terlalu rumit untuk menghapus semua file dalam direktori yang panjangnya di bawah x baris, dalam bash?
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 echo
ketika senang dengan apa yang akan dihapus.
Titik .
mewakili direktori saat ini. find
menemukan file dan direktori secara rekursif di dalam .
, dan dapat melakukan sesuatu dengan mereka.
-type
adalah 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 -exec
utama (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 find
perintah. Jadi -exec
panggilan 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 -v
bendera untuk AWK memungkinkan kita untuk mengatur variabel AWK (sekali) sebelum perintah AWK dieksekusi (untuk setiap baris dari file.) Dalam hal ini kita set x
ke 10
.
NR
adalah 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.
-exec
Panggilan berikutnya (jika Anda menghapus echo
) akan menghapus setiap file (yang sejauh itu dalam penilaian find
pendahuluan) dengan menjalankan:
rm -f ./somefilename
Dengan asumsi find
implementasi yang mendukung -readable
predikat (jika Anda find
tidak 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 echo
jika senang.
Itu tidak terlalu efisien dalam hal itu penting semua baris dalam setiap file sementara hanya perlu berhenti di x
th 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 echo
saat 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 print
dengan unlink
jika senang.
sh
? 2. Apakahwc -l < "$file"
lebih cepat daripadawc -l "$file"
? 3. Bagaimana dia tahu nilai$x
, yang didefinisikan dalam Bash shell panggilan?sh
adalah 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 berjalanwc
bahkan jika file tidak dapat dibuka.$x
diekspor kefind
(x=10 find...
) yang dengan sendirinya meneruskannya kesh
.find: -readable: unknown primary or operator
.bash
.bash
hanyalah penerjemah baris perintah, tetapifind
implementasinya.-readable
adalah ekstensi GNU, tidak tersedia di OS / Xfind
. 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 untukwc
file yang tidak dapat dibaca.Demi kelengkapan, selain AWK Anda juga dapat menggunakan GNU sed untuk mencapai hasil yang sama:
Yang menghasilkan baris perintah yang lebih ringkas.
Penjelasan
sumber