Pertanyaan ini terinspirasi oleh
Mengapa menggunakan shell loop untuk memproses teks dianggap praktik 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-posting yang menjelaskan mengapa hal semacam ini harus dihindari ...
Melihat jumlah posting seperti itu (dan fakta bahwa kadang-kadang komentar tersebut diabaikan) Saya pikir saya mungkin juga mengajukan pertanyaan:
Mengapa mengulangi find
praktik buruk keluaran dan apa cara yang tepat untuk menjalankan satu atau beberapa perintah untuk setiap nama file / jalur yang dikembalikan oleh find
?
Jawaban:
Masalah
menggabungkan dua hal yang tidak kompatibel.
find
mencetak daftar jalur file yang dibatasi oleh karakter baris baru. Sementara operator split + glob yang dipanggil ketika Anda membiarkan tanda$(find .)
kutip dalam konteks daftar membaginya pada karakter$IFS
(secara default termasuk baris baru, tetapi juga spasi dan tab (dan NUL dalamzsh
)) dan melakukan globbing pada setiap kata yang dihasilkan (kecuali inzsh
) (dan bahkan menguatkan ekspansi di turunan ksh93 atau pdksh!).Bahkan jika Anda berhasil:
Itu masih salah karena karakter baris baru sama validnya dengan yang ada di jalur file. Output dari
find -print
tidak pas-proses dapat diandalkan (kecuali dengan menggunakan beberapa trik berbelit-belit, seperti yang ditunjukkan di sini ).Itu juga berarti shell perlu menyimpan output
find
sepenuhnya, dan kemudian membagi + glob itu (yang menyiratkan menyimpan output yang kedua kalinya dalam memori) sebelum mulai untuk mengulang file.Catatan yang
find . | xargs cmd
memiliki masalah serupa (ada, kosong, baris baru, kutipan tunggal, kutipan ganda dan backslash (dan dengan beberapaxarg
byte implementasi tidak membentuk bagian dari karakter yang valid) adalah masalah)Alternatif yang lebih benar
Satu-satunya cara untuk menggunakan
for
loop pada outputfind
adalah dengan menggunakanzsh
yang mendukungIFS=$'\0'
dan:(ganti
-print0
dengan-exec printf '%s\0' {} +
untukfind
implementasi yang tidak mendukung non-standar (tapi cukup umum saat ini)-print0
).Di sini, cara yang benar dan portabel adalah dengan menggunakan
-exec
:Atau jika
something
dapat mengambil lebih dari satu argumen:Jika Anda membutuhkan daftar file yang akan ditangani oleh shell:
(Waspadalah mungkin mulai lebih dari satu
sh
).Pada beberapa sistem, Anda dapat menggunakan:
meskipun memiliki sedikit keuntungan atas sintaks standar dan berarti
something
'sstdin
adalah baik pipa atau/dev/null
.Salah satu alasan Anda mungkin ingin menggunakannya adalah menggunakan
-P
opsi GNUxargs
untuk pemrosesan paralel. Thestdin
Masalah juga dapat bekerja di sekitar dengan GNUxargs
dengan-a
pilihan dengan kerang mendukung substitusi proses:misalnya, untuk menjalankan hingga 4 doa bersamaan dari
something
masing - masing mengambil 20 argumen file.Dengan
zsh
ataubash
, cara lain untuk mengulang keluaranfind -print0
adalah dengan:read -d ''
membaca NUL catatan terbatas bukan yang dibatasi baris baru.bash-4.4
dan di atas juga dapat menyimpan file yang dikembalikan olehfind -print0
dalam array dengan:The
zsh
setara (yang memiliki keuntungan dari melestarikanfind
's status exit):Dengan
zsh
, Anda dapat menerjemahkan sebagian besarfind
ekspresi ke kombinasi globbing rekursif dengan kualifikasi glob. Misalnya, pengulanganfind . -name '*.txt' -type f -mtime -1
adalah:Atau
(waspadalah terhadap perlunya
--
dengan**/*
, jalur file tidak dimulai dengan./
, jadi mungkin mulai dengan-
misalnya).ksh93
danbash
akhirnya menambahkan dukungan untuk**/
(meskipun tidak lebih memajukan bentuk globbing rekursif), tetapi masih bukan kualifikasi glob yang membuat penggunaan**
sangat terbatas di sana. Hati-hati juga bahwabash
sebelum 4.3 mengikuti symlink ketika turun pohon direktori.Seperti untuk mengulang
$(find .)
, itu juga berarti menyimpan seluruh daftar file dalam memori 1 . Itu mungkin diinginkan meskipun dalam beberapa kasus ketika Anda tidak ingin tindakan Anda pada file memiliki pengaruh pada pencarian file (seperti ketika Anda menambahkan lebih banyak file yang akhirnya dapat ditemukan sendiri).Pertimbangan keandalan / keamanan lainnya
Kondisi balapan
Sekarang, jika kita berbicara tentang keandalan, kita harus menyebutkan kondisi balapan antara waktu
find
/zsh
menemukan file dan memeriksa apakah itu memenuhi kriteria dan waktu itu digunakan ( perlombaan TOCTOU ).Bahkan ketika menurunkan pohon direktori, kita harus memastikan untuk tidak mengikuti symlink dan melakukannya tanpa perlombaan TOCTOU.
find
(find
Setidaknya GNU ) melakukannya dengan membuka direktori menggunakanopenat()
denganO_NOFOLLOW
flag yang tepat (jika didukung) dan menjaga deskriptor file terbuka untuk setiap direktori,zsh
/bash
/ksh
jangan lakukan itu. Jadi, di hadapan penyerang yang bisa mengganti direktori dengan symlink pada waktu yang tepat, Anda bisa berakhir pada direktori yang salah.Sekalipun
find
turun direktori dengan benar, dengan-exec cmd {} \;
dan bahkan lebih lagi dengan-exec cmd {} +
, sekalicmd
dieksekusi, misalnya sebagaicmd ./foo/bar
ataucmd ./foo/bar ./foo/bar/baz
, pada saatcmd
memanfaatkan./foo/bar
, atributbar
mungkin tidak lagi memenuhi kriteria yang cocok denganfind
, tetapi lebih buruk lagi,./foo
mungkin telah digantikan oleh symlink ke beberapa tempat lain (dan jendela balapan dibuat jauh lebih besar dengan-exec {} +
tempatfind
menunggu untuk memiliki cukup file untuk dipanggilcmd
).Beberapa
find
implementasi memiliki-execdir
predikat ( belum standar) untuk mengatasi masalah kedua.Dengan:
find
chdir()
s ke direktori induk file sebelum menjalankancmd
. Alih-alih memanggilcmd -- ./foo/bar
, ia memanggilcmd -- ./bar
(cmd -- bar
dengan beberapa implementasi, maka itu--
), sehingga masalah dengan./foo
diubah menjadi symlink dihindari. Itu membuat menggunakan perintah sepertirm
lebih aman (itu masih bisa menghapus file yang berbeda, tetapi bukan file di direktori yang berbeda), tetapi bukan perintah yang dapat memodifikasi file kecuali mereka dirancang untuk tidak mengikuti symlink.-execdir cmd -- {} +
terkadang juga berfungsi tetapi dengan beberapa implementasi termasuk beberapa versi GNUfind
, itu setara dengan-execdir cmd -- {} \;
.-execdir
juga memiliki manfaat mengatasi beberapa masalah yang terkait dengan pohon direktori terlalu dalam.Di:
ukuran lintasan yang diberikan untuk
cmd
akan tumbuh dengan kedalaman direktori file. Jika ukuran itu menjadi lebih besar dariPATH_MAX
(sesuatu seperti 4k di Linux), maka setiap panggilan sistem yangcmd
dilakukan pada lintasan itu akan gagal denganENAMETOOLONG
kesalahan.Dengan
-execdir
, hanya nama file (mungkin diawali dengan./
) dilewatkan kecmd
. Nama file sendiri pada sebagian besar sistem file memiliki batas (NAME_MAX
) yang jauh lebih rendah daripadaPATH_MAX
, sehinggaENAMETOOLONG
kesalahan lebih kecil kemungkinannya terjadi.Bytes vs karakter
Juga, sering diabaikan ketika mempertimbangkan keamanan sekitar
find
dan lebih umum dengan menangani nama file secara umum adalah kenyataan bahwa pada kebanyakan sistem mirip Unix, nama file adalah urutan byte (nilai byte apa pun kecuali 0 dalam jalur file, dan pada sebagian besar sistem ( Yang berbasis ASCII, kami akan mengabaikan yang berbasis langka EBCDIC untuk saat ini) 0x2f adalah pembatas jalur).Terserah aplikasi untuk memutuskan apakah mereka ingin mempertimbangkan byte tersebut sebagai teks. Dan mereka umumnya melakukannya, tetapi umumnya terjemahan dari byte ke karakter dilakukan berdasarkan lokal pengguna, berdasarkan lingkungan.
Apa itu artinya bahwa nama file yang diberikan mungkin memiliki representasi teks yang berbeda tergantung pada lokal. Sebagai contoh, urutan byte
63 f4 74 e9 2e 74 78 74
akancôté.txt
untuk aplikasi yang menafsirkan nama file itu di lokal di mana set karakter adalah ISO-8859-1, dancєtщ.txt
di lokal di mana charset adalah IS0-8859-5 sebagai gantinya.Lebih buruk. Di lokal di mana charset adalah UTF-8 (norma saat ini), 63 f4 74 e9 2e 74 78 74 tidak bisa dipetakan ke karakter!
find
adalah salah satu aplikasi tersebut yang menganggap nama file sebagai teks untuk nya-name
/-path
predikat (dan lebih, seperti-iname
atau-regex
dengan beberapa implementasi).Apa artinya itu misalnya, dengan beberapa
find
implementasi (termasuk GNUfind
).tidak akan menemukan
63 f4 74 e9 2e 74 78 74
file kami di atas ketika dipanggil di lokal UTF-8 karena*
(yang cocok dengan 0 atau lebih karakter , bukan byte) tidak dapat cocok dengan yang bukan karakter.LC_ALL=C find...
akan mengatasi masalah karena C locale menyiratkan satu byte per karakter dan (umumnya) menjamin bahwa semua nilai byte memetakan ke karakter (meskipun mungkin tidak terdefinisi untuk beberapa nilai byte).Sekarang ketika datang untuk mengulangi nama-nama file dari shell, byte vs karakter itu juga bisa menjadi masalah. Kami biasanya melihat 4 jenis kerang utama dalam hal ini:
Yang masih belum sadar multi-byte suka
dash
. Bagi mereka, byte memetakan sebuah karakter. Misalnya, dalam UTF-8,côté
adalah 4 karakter, tetapi 6 byte. Di lokal tempat UTF-8 adalah rangkaian karakter, difind
akan berhasil menemukan file yang namanya terdiri dari 4 karakter yang dikodekan dalam UTF-8, tetapidash
akan melaporkan panjangnya berkisar antara 4 dan 24.yash
: sebaliknya. Ini hanya berurusan dengan karakter . Semua input yang dibutuhkan diterjemahkan secara internal ke karakter. Itu membuat shell yang paling konsisten, tetapi juga berarti ia tidak bisa mengatasi urutan byte sewenang-wenang (yang tidak diterjemahkan ke karakter yang valid). Bahkan di lokal C, ia tidak bisa mengatasi nilai byte di atas 0x7f.di lokal UTF-8 akan gagal pada ISO-8859-1 kami
côté.txt
dari sebelumnya misalnya.Mereka yang suka
bash
atau dizsh
mana dukungan multi-byte telah semakin ditambahkan. Itu akan kembali ke mengingat byte yang tidak dapat dipetakan ke karakter seolah-olah mereka adalah karakter. Mereka masih memiliki beberapa bug di sana-sini terutama dengan charset multi-byte yang kurang umum seperti GBK atau BIG5-HKSCS (yang cukup buruk karena banyak karakter multi-byte mereka mengandung byte dalam rentang 0-127 (seperti karakter ASCII) ).Mereka seperti
sh
FreeBSD (setidaknya 11) ataumksh -o utf8-mode
yang mendukung multi-byte tetapi hanya untuk UTF-8.Catatan
1 Untuk kelengkapan, kami dapat menyebutkan cara hacky
zsh
untuk mengulang file menggunakan globing rekursif tanpa menyimpan seluruh daftar dalam memori:+cmd
adalah kualifikasi global yang memanggilcmd
(biasanya suatu fungsi) dengan jalur file saat ini di$REPLY
. Fungsi mengembalikan benar atau salah untuk memutuskan apakah file harus dipilih (dan juga dapat mengubah$REPLY
atau mengembalikan beberapa file dalam$reply
array). Di sini kita melakukan pemrosesan dalam fungsi itu dan mengembalikan false sehingga file tidak dipilih.sumber
find
perilaku menjadi aman. Globbing aman secara default sedangkan find tidak aman secara default.Jawaban sederhananya adalah:
Karena nama file dapat mengandung karakter apa saja .
Oleh karena itu, tidak ada karakter yang dapat dicetak yang dapat Anda gunakan untuk membatasi nama file.
Baris baru sering digunakan (secara tidak benar) untuk membatasi nama file, karena tidak biasa untuk memasukkan karakter baris baru dalam nama file.
Namun, jika Anda membangun perangkat lunak berdasarkan asumsi sewenang-wenang, Anda paling tidak gagal menangani kasus-kasus yang tidak biasa, dan paling buruk membuka diri Anda sendiri untuk eksploit jahat yang memberikan kendali penuh atas sistem Anda. Jadi ini pertanyaan tentang ketahanan dan keamanan.
Jika Anda dapat menulis perangkat lunak dengan dua cara berbeda, dan salah satunya menangani kasus tepi (input tidak biasa) dengan benar, tetapi yang lain lebih mudah dibaca, Anda mungkin berpendapat bahwa ada tradeoff. (Saya tidak mau. Saya lebih suka kode yang benar.)
Namun, jika versi kode yang benar dan kuat juga mudah dibaca, tidak ada alasan untuk menulis kode yang gagal pada kasus tepi. Ini adalah kasus dengan
find
dan kebutuhan untuk menjalankan perintah pada setiap file yang ditemukan.Mari kita lebih spesifik: Pada sistem UNIX atau Linux, nama file dapat berisi karakter apa pun kecuali untuk
/
(yang digunakan sebagai pemisah komponen jalur), dan mereka mungkin tidak mengandung byte nol.Oleh karena itu byte nol adalah satu - satunya cara yang benar untuk membatasi nama file.
Karena GNU
find
menyertakan-print0
primer yang akan menggunakan byte nol untuk membatasi nama file yang dicetaknya, GNUfind
dapat digunakan dengan aman dengan GNUxargs
dan-0
benderanya (dan-r
benderanya) untuk menangani output darifind
:Namun, tidak ada alasan yang baik untuk menggunakan formulir ini, karena:
find
adalah dirancang untuk dapat menjalankan perintah pada file yang ditemukan.Selain itu, GNU
xargs
memerlukan-0
dan-r
, sedangkan FreeBSDxargs
hanya membutuhkan-0
(dan tidak memiliki-r
opsi), dan beberapaxargs
tidak mendukung-0
sama sekali. Jadi yang terbaik adalah tetap menggunakan fitur POSIXfind
(lihat bagian selanjutnya) dan lewatixargs
.Adapun
find
kemampuan poin 2— untuk menjalankan perintah pada file yang ditemukannya — saya pikir Mike Loukides mengatakan yang terbaik:POSIX menggunakan spesifik dari
find
Untuk menjalankan satu perintah untuk setiap file yang ditemukan, gunakan:
Untuk menjalankan beberapa perintah secara berurutan untuk setiap file yang ditemukan, di mana perintah kedua hanya dapat dijalankan jika perintah pertama berhasil, gunakan:
Untuk menjalankan satu perintah pada banyak file sekaligus:
find
dalam kombinasi dengansh
Jika Anda perlu menggunakan fitur shell dalam perintah, seperti mengarahkan ulang output atau melepas ekstensi dari nama file atau yang serupa, Anda dapat menggunakan
sh -c
konstruk. Anda harus tahu beberapa hal tentang ini:Jangan pernah menyematkan
{}
langsung dalamsh
kode. Ini memungkinkan untuk eksekusi kode arbitrer dari nama file yang dibuat dengan jahat. Juga, itu sebenarnya bahkan tidak ditentukan oleh POSIX bahwa itu akan berfungsi sama sekali. (Lihat poin berikutnya.)Jangan gunakan
{}
beberapa kali, atau gunakan itu sebagai bagian dari argumen yang lebih panjang. Ini tidak portabel. Misalnya, jangan lakukan ini:find ... -exec cp {} somedir/{}.bak \;
Mengutip spesifikasi POSIX untuk
find
:Argumen yang mengikuti string perintah shell yang diteruskan ke
-c
opsi diatur ke parameter posisi shell, dimulai dengan$0
. Tidak dimulai dengan$1
.Untuk alasan ini, ada baiknya untuk memasukkan nilai "dummy"
$0
, sepertifind-sh
, yang akan digunakan untuk pelaporan kesalahan dari dalam shell yang dihasilkan. Juga, ini memungkinkan penggunaan konstruksi seperti"$@"
ketika mengirimkan beberapa file ke shell, sedangkan menghilangkan nilai untuk$0
berarti file pertama yang dilewati akan diatur ke$0
dan dengan demikian tidak termasuk dalam"$@"
.Untuk menjalankan perintah shell tunggal per file, gunakan:
Namun biasanya ini akan memberikan kinerja yang lebih baik untuk menangani file-file dalam loop shell sehingga Anda tidak menelurkan shell untuk setiap file yang ditemukan:
(Catatan yang
for f do
setara denganfor f in "$@"; do
dan menangani masing-masing parameter posisi pada gilirannya — dengan kata lain, ia menggunakan setiap file yang ditemukan olehfind
, terlepas dari karakter khusus apa pun dalam namanya).Contoh lebih lanjut tentang
find
penggunaan yang benar :(Catatan: Jangan ragu untuk memperpanjang daftar ini.)
sumber
find
output - di mana Anda perlu menjalankan perintah di shell saat ini (misalnya karena Anda ingin mengatur variabel) untuk setiap file. Dalam hal ini,while IFS= read -r -u3 -d '' file; do ... done 3< <(find ... -print0)
adalah idiom terbaik yang saya tahu. Catatan:<( )
tidak portabel - gunakan bash atau zsh. Juga,-u3
dan3<
apakah ada sesuatu dalam loop mencoba membaca stdin.find ... -exec
panggilan. Atau cukup gunakan shell glob, jika itu akan menangani use case Anda.filelist=(); while ... do filelist+=("$file"); done ...
).find
atau bahkan lebih buruk menggunakanls
. Saya melakukan ini setiap hari tanpa masalah. Saya tahu tentang opsi -print0, --null, -z atau -0 dari semua jenis alat. Tetapi saya tidak akan membuang waktu untuk menggunakannya pada prompt shell interaktif saya kecuali benar-benar diperlukan. Ini bisa juga dicatat dalam jawaban Anda.Jawaban ini untuk set hasil yang sangat besar dan terutama menyangkut kinerja, misalnya ketika mendapatkan daftar file melalui jaringan yang lambat. Untuk sejumlah kecil file (katakan beberapa 100 atau bahkan mungkin 1000 pada disk lokal) kebanyakan ini diperdebatkan.
Paralelisme dan penggunaan memori
Selain dari jawaban lain yang diberikan, terkait dengan masalah pemisahan dan semacamnya, ada masalah lain dengan
Bagian di dalam backticks harus dievaluasi sepenuhnya terlebih dahulu, sebelum dipisah pada linebreak. Ini berarti, jika Anda mendapatkan sejumlah besar file, itu bisa tersedak batas ukuran apa pun yang ada di berbagai komponen; Anda dapat kehabisan memori jika tidak ada batasan; dan dalam hal apa pun Anda harus menunggu sampai seluruh daftar telah di-output oleh
find
dan kemudian diuraikanfor
sebelum bahkan menjalankan yang pertamasmth
.Cara unix yang disukai adalah bekerja dengan pipa, yang secara inheren berjalan secara paralel, dan yang juga tidak perlu buffer besar secara sewenang-wenang pada umumnya. Itu berarti: Anda akan lebih suka
find
menjalankannya secara paralel dengan Andasmth
, dan hanya menyimpan nama file saat ini di RAM sementara itu menyerahkan kesmth
.Salah satu setidaknya sebagian solusi OKI untuk itu adalah yang disebutkan di atas
find -exec smth
. Ini menghilangkan kebutuhan untuk menjaga semua nama file dalam memori dan berjalan dengan baik secara paralel. Sayangnya, ini juga memulai satusmth
proses per file. Jikasmth
hanya bisa bekerja pada satu file, maka memang sudah seharusnya begitu.Jika memungkinkan, solusi optimal adalah
find -print0 | smth
, dengansmth
dapat memproses nama file pada STDIN-nya. Maka Anda hanya memiliki satusmth
proses tidak peduli berapa banyak file yang ada, dan Anda perlu buffer hanya sejumlah kecil byte (apa pun penyangga pipa intrinsik yang terjadi) antara kedua proses. Tentu saja, ini agak tidak realistis jikasmth
merupakan perintah standar Unix / POSIX, tetapi mungkin pendekatan jika Anda menulisnya sendiri.Jika itu tidak mungkin, maka
find -print0 | xargs -0 smth
kemungkinan besar adalah salah satu solusi yang lebih baik. Seperti @ dave_thompson_085 disebutkan dalam komentar,xargs
tidak membagi argumen di beberapa berjalansmth
ketika batas sistem tercapai (secara default, dalam kisaran 128 KB atau batas apa pun yang dikenakanexec
pada sistem), dan memiliki opsi untuk mempengaruhi berapa banyak file diberikan ke satu panggilansmth
, karenanya menemukan keseimbangan antara jumlahsmth
proses dan penundaan awal.EDIT: menghapus gagasan "terbaik" - sulit untuk mengatakan apakah sesuatu yang lebih baik akan muncul. ;)
sumber
find ... -exec smth {} +
adalah solusinya.find -print0 | xargs smth
tidak bekerja sama sekali, tetapifind -print0 | xargs -0 smth
(catatan-0
) ataufind | xargs smth
jika nama file tidak memiliki tanda kutip atau backslash menjalankan satusmth
dengan nama file sebanyak yang tersedia dan cocok dalam satu daftar argumen ; jika Anda melebihi maxargs, itu berjalansmth
sebanyak yang diperlukan untuk menangani semua argumen yang diberikan (tanpa batas). Anda dapat mengatur 'potongan' yang lebih kecil (dengan demikian paralelisme sebelumnya)-L/--max-lines -n/--max-args -s/--max-chars
.Salah satu alasannya adalah bahwa spasi putih melemparkan kunci pas dalam karya, membuat file 'foo bar' dievaluasi sebagai 'foo' dan 'bar'.
Bekerja ok jika -exec digunakan sebagai gantinya
sumber
find
karena ada opsi untuk mengeksekusi perintah pada setiap file itu dengan mudah pilihan terbaik.-exec ... {} \;
versus-exec ... {} +
for file in "$(find . -type f)"
danecho "${file}"
kemudian itu bekerja bahkan dengan spasi putih, karakter khusus lainnya saya kira menyebabkan lebih banyak masalahfor file in "$(find . -type f)";do printf '%s %s\n' name: "${file}";done
yang harus (menurut Anda) mencetak setiap nama file pada baris terpisah yang didahului olehname:
. Tidak.Karena output dari perintah apa pun adalah string tunggal, tetapi loop Anda memerlukan array string untuk mengulang. Alasannya "bekerja" adalah bahwa kerang mengkhianati membagi string pada spasi untuk Anda.
Kedua, kecuali Anda memerlukan fitur tertentu
find
, sadari bahwa shell Anda kemungkinan besar sudah dapat mengembangkan pola gumpalan rekursif dengan sendirinya, dan yang terpenting, shell akan diperluas ke array yang tepat.Contoh bash:
Sama dengan Ikan:
Jika Anda memang membutuhkan fitur
find
, pastikan untuk hanya membagi pada NUL (sepertifind -print0 | xargs -r0
idiom).Ikan dapat mengulangi keluaran terbatas NUL. Jadi yang ini sebenarnya tidak buruk:
Sebagai gotcha kecil terakhir, dalam banyak shell (bukan Fish tentu saja), perulangan keluaran perintah akan membuat tubuh perulangan menjadi subkulit (artinya Anda tidak dapat menetapkan variabel dengan cara apa pun yang terlihat setelah loop berakhir), yang merupakan tidak pernah seperti yang kamu inginkan.
sumber
zsh
awal 90-an (meskipun Anda perlu di**/*
sana).fish
seperti implementasi sebelumnya dari fitur setara bash mengikuti symlink ketika turun pohon direktori. Lihat Hasil ls *, ls ** dan ls *** untuk perbedaan antara implementasi.Melonggarkan hasil temuan bukanlah praktik yang buruk — praktik yang buruk (dalam situasi ini & semua situasi) mengasumsikan input Anda adalah format tertentu alih-alih mengetahui (menguji & mengonfirmasi) itu adalah format tertentu.
tldr / cbf:
find | parallel stuff
sumber