Saya sedang mengerjakan skrip yang perlu melakukan tindakan di setiap sub-direktori folder tertentu.
Apa cara paling efisien untuk menulis itu?
bash
command
directory-traversal
mikewilliamson
sumber
sumber
mkdir 'foo * bar'
akan menyebabkanfoo
danbar
untuk diulang bahkan jika mereka tidak ada, dan*
akan diganti dengan daftar semua nama file, bahkan yang bukan direktori).mkdir -p '/tmp/ /etc/passwd /'
- jika seseorang menjalankan skrip yang mengikuti praktik ini/tmp
ke, katakanlah, temukan direktori untuk dihapus, mereka bisa berakhir dengan menghapus/etc/passwd
.Jawaban:
sumber
find
params jika Anda ingin perilaku rekursif atau non-rekursif.find . -mindepth 1 -type d
mkdir 'directory name with spaces'
menjadi empat kata terpisah.-mindepth 1 -maxdepth 1
atau terlalu dalam.Versi yang menghindari pembuatan sub-proses:
Atau, jika tindakan Anda adalah satu perintah, ini lebih ringkas:
Atau versi yang lebih ringkas ( terima kasih @enzotib ). Perhatikan bahwa dalam versi ini setiap nilai
D
akan memiliki garis miring:sumber
if
atau[
dengan:for D in */; do
for D in *; do [ -d "${D}" ] && my_command; done
atau kombinasi dari dua yang terbaru:for D in */; do [ -d $D ] && my_command; done
for D in .* *; do
sajafor D in *; do
.Cara non rekursif yang paling sederhana adalah:
Di
/
bagian akhir memberitahu, gunakan direktori saja.Tidak perlu
sumber
shopt -s dotglob
untuk memasukkan dotfiles / dotdirs ketika memperluas wildcard. Lihat juga: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html/*
alih-alih*/
dengan/
mewakili jalur yang ingin Anda gunakan./*
akan menjadi jalur absolut sedangkan*/
akan mencakup subdirektori dari lokasi saat ini${d%/*}
Gunakan
find
perintah.Di GNU
find
, Anda dapat menggunakan-execdir
parameter:atau dengan menggunakan
-exec
parameter:atau dengan
xargs
perintah:Atau gunakan untuk loop:
Untuk rekursivitas coba globbing diperpanjang (
**/
) sebagai gantinya (diaktifkan oleh:)shopt -s extglob
.Untuk lebih banyak contoh, lihat: Cara pergi ke setiap direktori dan menjalankan perintah? di SO
sumber
-exec {} +
ditentukan POSIX,-exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +
adalah opsi hukum lain, dan meminta lebih sedikit shell-exec sh -c '...' {} \;
.Satu garis yang praktis
Opsi A benar untuk folder dengan spasi di antaranya. Juga, umumnya lebih cepat karena tidak mencetak setiap kata dalam nama folder sebagai entitas yang terpisah.
sumber
find . -type d -print0 | xargs -0 -n 1 my_command
sumber
my_command
.Ini akan membuat subkulit (yang berarti bahwa nilai variabel akan hilang ketika
while
loop keluar):Ini tidak akan:
Salah satu akan bekerja jika ada spasi dalam nama direktori.
sumber
find ... -print0
danwhile IFS="" read -r -d $'\000' dir
-d ''
kurang menyesatkan tentang sintaks dan kemampuan bash, karena-d $'\000'
menyiratkan (salah) yang$'\000'
dalam beberapa cara berbeda dari''
- memang, seseorang dapat dengan mudah (dan sekali lagi, palsu) disimpulkan dari itu bash mendukung string gaya Pascal (panjang ditentukan, mampu mengandung NUL literal) daripada string C (NUL dibatasi, tidak dapat mengandung NUL).Kamu bisa mencoba:
atau serupa ...
Penjelasan:
find . -maxdepth 1 -mindepth 1 -type d
: Hanya temukan direktori dengan kedalaman rekursif maksimum 1 (hanya subdirektori $ 1) dan kedalaman minimum 1 (tidak termasuk folder saat ini.
)sumber
cd "$f"
. Ini tidak berfungsi ketika output darifind
string-split, jadi Anda akan memiliki potongan-potongan nama yang terpisah sebagai nilai-nilai terpisah$f
, membuat seberapa baik Anda melakukan atau tidak mengutip$f
moot ekspansi.find
Outputnya berorientasi garis (satu nama ke baris, dalam teori - tetapi lihat di bawah) dengan-print
aksi default .find -print
itu bukan cara yang aman untuk melewati nama file yang sewenang-wenang, karena seseorang dapat menjalankan sesuatumkdir -p $'foo\n/etc/passwd\nbar'
dan mendapatkan direktori yang memiliki/etc/passwd
baris terpisah dalam namanya. Menangani nama dari file dalam/upload
atau/tmp
direktori tanpa perawatan adalah cara yang bagus untuk mendapatkan serangan eskalasi hak istimewa.jawaban yang diterima akan pecah pada spasi putih jika nama direktori memilikinya, dan sintaks yang disukai adalah
$()
untuk bash / ksh. GunakanGNU find
-exec
opsi dengan+;
misalnyafind .... -exec mycommand +;
#this is same as passing to xargs
atau gunakan loop sementara
sumber
find -exec
dan meneruskan kexargs
:find
akan mengabaikan nilai keluar dari perintah yang dieksekusi, sementaraxargs
akan gagal pada jalan keluar yang bukan nol. Mungkin benar, tergantung pada kebutuhan Anda.find ... -print0 | while IFS= read -r d
lebih aman - mendukung nama yang dimulai atau berakhir di spasi putih, dan nama yang berisi literal baris baru.