Pertanyaan ini terinspirasi oleh
Mengapa menggunakan loop shell untuk memproses teks dianggap sebagai praktik yang buruk?
Saya melihat konstruksi ini
for file in `find . -type f -name ...`; do smth with ${file}; done
dan
for dir in $(find . -type d -name ...); do smth with ${dir}; done
digunakan di sini hampir setiap hari bahkan jika beberapa orang meluangkan waktu untuk mengomentari posting tersebut menjelaskan mengapa hal semacam ini harus dihindari…
Melihat jumlah posting tersebut (dan fakta bahwa kadang-kadang komentar tersebut hanya diabaikan) Saya pikir sebaiknya saya mengajukan pertanyaan:
Mengapa mengulang find
praktik buruk keluaran dan apa cara yang tepat untuk menjalankan satu atau lebih perintah untuk setiap nama file/jalur yang dikembalikan oleh find
?
Jawaban yang Diterima:
Masalahnya
for f in $(find .)
menggabungkan dua hal yang tidak kompatibel.
find
mencetak daftar jalur file yang dibatasi oleh karakter baris baru. Sementara operator split+glob yang dipanggil saat Anda meninggalkan $(find .)
tidak dikutip dalam konteks daftar itu membaginya pada karakter $IFS
(secara default termasuk baris baru, tetapi juga spasi dan tab (dan NUL di zsh
)) dan melakukan globbing pada setiap kata yang dihasilkan (kecuali dalam zsh
) (dan bahkan menahan ekspansi dalam turunan ksh93 atau pdksh!).
Bahkan jika Anda berhasil:
IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
# but not ksh93)
for f in $(find .) # invoke split+glob
Itu masih salah karena karakter baris baru sama validnya dengan apa pun di jalur file. Output dari find -print
sama sekali tidak dapat diandalkan pasca-proses (kecuali dengan menggunakan beberapa trik yang berbelit-belit, seperti yang ditunjukkan di sini ).
Itu juga berarti shell perlu menyimpan output dari find
sepenuhnya, lalu split+glob (yang berarti menyimpan output itu untuk kedua kalinya dalam memori) sebelum mulai mengulang file.
Perhatikan bahwa find . | xargs cmd
memiliki masalah yang sama (ada, kosong, baris baru, kutipan tunggal, kutipan ganda dan garis miring terbalik (dan dengan beberapa xarg
byte implementasi yang tidak membentuk bagian dari karakter yang valid) adalah masalah)
Alternatif yang lebih tepat
Satu-satunya cara untuk menggunakan for
loop pada output find
akan menggunakan zsh
yang mendukung IFS=$'