Bisakah seseorang memberikan kode untuk melakukan hal berikut: Asumsikan ada direktori file, semua yang perlu dijalankan melalui sebuah program. Program output hasil untuk standar keluar. Saya membutuhkan sebuah script yang akan pergi ke sebuah direktori, mengeksekusi perintah pada masing-masing file, dan concat output ke dalam satu file output.
Misalnya, untuk menjalankan perintah pada 1 file:
$ cmd [option] [filename] > results.out
Berikut kode bash akan melewati $file dengan perintah mana $file yang akan mewakili setiap file /dir
for file in /dir/*
do
cmd [option] "$file" >> results.out
done
Contoh
el@defiant ~/foo $ touch foo.txt bar.txt baz.txt
el@defiant ~/foo $ for i in *.txt; do echo "hello $i"; done
hello bar.txt
hello baz.txt
hello foo.txt
Bagaimana dengan ini:
find /some/directory -maxdepth 1 -type f -exec cmd option {} \; > results.out
-maxdepth 1
argumen mencegah menemukan dari rekursif turun ke
setiap subdirektori. (Jika anda ingin seperti bersarang direktori untuk mendapatkan diproses, anda bisa mengabaikan hal ini.)-type-f
menetapkan bahwa hanya polos file yang akan diproses.-exec cmd pilihan {}
mengatakan itu untuk menjalankan cmd
ditentukan pilihan
untuk setiap file yang ditemukan, dengan nama file diganti untuk {}
\;
menunjukkan akhir perintah.cmd
eksekusi diarahkan untuk
hasil.keluar
Namun, jika anda peduli tentang urutan file yang akan diproses, anda
mungkin akan lebih baik menulis sebuah loop. Saya pikir menemukan
proses file
dalam inode order (meskipun saya bisa saja salah tentang itu), yang mungkin tidak akan apa yang
anda ingin.
Yang saya butuhkan untuk menyalin semua .md file dari satu direktori ke yang lain, jadi di sini adalah apa yang saya lakukan.
bagi saya di **/*.md;melakukan mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i", "../docs/$i" && echo "$i -> ../docs/$i"; dilakukan
Yang cukup sulit untuk dibaca, jadi mari kita memecahnya.
cd pertama ke direktori dengan file anda,
bagi saya di **/*.md;
untuk setiap file dalam pola anda
mkdir -p ../docs/"$i"
membuat direktori dalam dokumen folder di luar folder yang berisi file anda. Yang menciptakan tambahan folder dengan nama yang sama seperti file itu.
rm -r ../docs/"$i"
menghapus folder tambahan yang dibuat sebagai hasil dari mkdir -p
cp "$i", "../docs/$i"
Copy file yang sebenarnya
echo "$i -> ../docs/$i"
Echo apa yang anda lakukan
; dilakukan
Live happily ever after
Satu cepat dan cara kotor yang mendapatkan pekerjaan dilakukan kadang-kadang adalah:
find directory/ | xargs Command
Misalnya untuk mengetahui jumlah baris dalam semua file dalam direktori saat ini, anda dapat melakukan:
find . | xargs wc -l
Saya menemukan itu bekerja dengan baik dengan Jim Lewis's jawaban hanya menambahkan sedikit seperti ini:
$ export DIR=/path/dir && cd $DIR && chmod -R +x *
$ find . -maxdepth 1 -type f -name '*.sh' -exec {} \; > results.out
Jika anda ingin mengeksekusi dalam urutan, memodifikasi seperti ini:
$ export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -maxdepth 2 -type f -name '*.sh' | sort | bash > results.out
Hanya untuk contoh, ini akan mengeksekusi dengan urutan sebagai berikut:
bash: 1: ./assets/main.sh
bash: 2: ./builder/clean.sh
bash: 3: ./builder/concept/compose.sh
bash: 4: ./builder/concept/market.sh
bash: 5: ./builder/concept/services.sh
bash: 6: ./builder/curl.sh
bash: 7: ./builder/identity.sh
bash: 8: ./concept/compose.sh
bash: 9: ./concept/market.sh
bash: 10: ./concept/services.sh
bash: 11: ./product/compose.sh
bash: 12: ./product/market.sh
bash: 13: ./product/services.sh
bash: 14: ./xferlog.sh
Jika anda ingin mengeksekusi di kedalaman tak terbatas oleh kondisi tertentu, anda dapat menggunakan ini:
export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -type f -name '*.sh' | sort | bash > results.out
kemudian diletakkan di atas setiap file pada anak direktori seperti ini:
#!/bin/bash
[[ "$(dirname `pwd`)" == $DIR ]] && echo "Executing `realpath $0`.." || return
dan di suatu tempat di dalam tubuh induk file:
if <a condition is matched>
then
#execute child files
export DIR=`pwd`
fi
Diterima/tinggi-memilih jawaban yang bagus, tapi mereka kurang beberapa seluk-beluk detail. Posting ini mencakup kasus-kasus tentang bagaimana untuk menangani lebih baik ketika shell jalan-nama ekspansi (glob) gagal, ketika nama file yang berisi tertanam baris/dash simbol dan bergerak output perintah re-arah keluar untuk loop ketika menulis hasilnya ke file.
Ketika menjalankan shell glob ekspansi menggunakan *
ada kemungkinan untuk perluasan gagal jika ada no file-file yang ada di direktori, dan un-diperluas glob string akan dilewatkan ke perintah yang akan dijalankan, yang bisa memiliki hasil yang tidak diinginkan. The bash
shell menyediakan diperpanjang shell opsi ini untuk menggunakan nullglob
. Jadi loop pada dasarnya menjadi sebagai berikut dalam direktori yang berisi file anda
shopt -s nullglob
for file in ./*; do
cmdToRun [option] -- "$file"
done
Ini memungkinkan anda dengan aman keluar untuk loop ketika ekspresi ./*
tidak mengembalikan file ( jika direktori kosong)
atau dalam POSIX-compliant cara (nullglob
adalah bash
tertentu)
for file in ./*; do
[ -f "$file" ] || continue
cmdToRun [option] -- "$file"
done
Hal ini memungkinkan anda masuk ke dalam lingkaran ketika ekspresi gagal untuk sekali dan kondisi [ -f "$file" ]
check jika pbb yang diperluas string ./*
yang valid adalah nama file dalam direktori tersebut, yang tidak't akan. Sehingga pada kondisi ini kegagalan, menggunakan lanjutkan
kami melanjutkan kembali kepada para untuk
loop yang tidak't berjalan selanjutnya.
Perhatikan juga penggunaan --
sebelum lewat nama file argumen. Hal ini diperlukan karena seperti disebutkan sebelumnya, shell nama file dapat berisi tanda hubung di mana saja di nama file. Beberapa perintah shell menafsirkan itu dan memperlakukan mereka sebagai perintah pilihan ketika nama not dikutip dengan benar dan mengeksekusi perintah berpikir jika bendera yang disediakan.
The --
sinyal akhir dari baris perintah pilihan dalam hal yang berarti, perintah tidak't mengurai setiap string melampaui titik ini sebagai perintah bendera tetapi hanya sebagai nama file.
Double-mengutip nama file dengan benar memecahkan kasus-kasus ketika nama yang mengandung glob karakter atau putih-ruang. Tapi *nix nama file juga dapat berisi baris baru di dalamnya. Jadi kita de-batas nama file dengan satu-satunya karakter yang tidak bisa menjadi bagian dari nama berkas yang valid - null byte (\0
). Sejak bash
internal menggunakan C
style string di mana null byte yang digunakan untuk menunjukkan akhir dari string, adalah kandidat yang tepat untuk ini.
Jadi menggunakan printf
pilihan dari shell untuk membatasi file dengan NULL byte menggunakan -d
pilihan baca
command, yang bisa kita lakukan di bawah ini
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done
The nullglob
dan printf
melilit (..)
yang berarti mereka pada dasarnya berjalan di sub-shell (shell anak), karena untuk menghindari nullglob
pilihan untuk mencerminkan pada orang tua shell, setelah perintah keluar. The -d ''
pilihan baca
perintah not POSIX-compliant, sehingga membutuhkan bash
shell untuk ini harus dilakukan. Menggunakan menemukan
perintah ini dapat dilakukan sebagai
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)
Untuk menemukan
implementasi yang don't dukungan -print0
(lain dari GNU dan FreeBSD implementasi), hal ini dapat ditiru dengan menggunakan printf
find . -maxdepth 1 -type f -exec printf '%s\0' {} \; | xargs -0 cmdToRun [option] --
Lain memperbaiki penting adalah untuk memindahkan re-arah keluar untuk loop untuk mengurangi jumlah file I/O. Ketika digunakan di dalam loop, shell telah melaksanakan sistem-panggilan dua kali untuk masing-masing iterasi for-loop, sekali untuk membuka dan sekaligus untuk menutup file descriptor yang berhubungan dengan file. Ini akan menjadi leher botol pada kinerja anda untuk menjalankan besar iterasi. Direkomendasikan saran akan bergerak di luar lingkaran.
Memperluas kode di atas dengan perbaikan ini, anda bisa melakukan
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done > results.out
yang pada dasarnya akan menempatkan isi dari perintah anda untuk setiap iterasi dari file input ke stdout dan ketika loop berakhir, buka file target sekali untuk menulis isi dari stdout dan menyimpannya. Setara menemukan
versi yang sama akan
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out
Berdasarkan @Jim Lewis's pendekatan:
Berikut adalah solusi cepat menggunakan menemukan
dan juga menyortir file dengan tanggal modifikasi:
$ find directory/ -maxdepth 1 -type f -print0 | \
xargs -r0 stat -c "%y %n" | \
sort | cut -d' ' -f4- | \
xargs -d "\n" -I{} cmd -op1 {}
Untuk menyortir melihat:
http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time