Exec memungkinkan kita untuk melewati semua argumen sekaligus dengan {} +
atau meneruskannya satu per satu dengan{} \;
Sekarang katakanlah saya ingin mengganti nama semua jpeg , tidak masalah melakukan ini:
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec mv '{}' '{}'.new \;
Tetapi jika saya perlu mengarahkan ulang output, '{}'
tidak dapat diakses setelah pengalihan.
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjpeg -quality 80 '{}' > optimized_'{}' \;
Ini tidak akan berhasil. Saya harus menggunakan for for, menyimpan output find ke dalam variabel sebelum menggunakannya. Mari kita akui, itu merepotkan.
for f in `find . \( -name '*.jpg' -o -name '*.jpeg' \)`; do cjpeg -quality 80 $f > optimized_$f; done;
Jadi adakah cara yang lebih baik?
>
hilang dalam sampel kode ketiga?{}
muncul dalam string yang lebih panjang karena string seperti itu biasanya tidak diperluas.find
, satu kali, dan diterapkan padafind
perintah itu sendiri. Tidak{}
memiliki makna khusus dalam konteks itu. Pengalihan bukan argumen untukfind
, dan tentu saja bukan bagian dari-exec
klausa.Jawaban:
Anda bisa menggunakan
bash -c
dalamfind -exec
perintah dan menggunakan parameter posisi dengan perintah bash:Dengan cara
{}
itu disediakan$1
.The
sh
sebelum{}
menceritakan shell batin "nama" nya, string yang digunakan di sini digunakan dalam misalnya pesan kesalahan. Ini dibahas lebih lanjut dalam jawaban ini di stackoverflow .sumber
Anda punya jawaban ( https://unix.stackexchange.com/a/481687/4778 ), tetapi inilah sebabnya.
Pengalihan
>
, dan juga pipa|
, dan$
ekspansi, semua dilakukan oleh shell sebelum perintah dieksekusi. Oleh karena itu stdout dialihkan keoptimized_{}
, sebelumfind
dimulai.sumber
Pengalihan perlu dikutip untuk menghindari bahwa shell yang sekarang menafsirkannya.
Tetapi mengutipnya juga akan menghindari output dari perintah untuk diarahkan.
Solusi yang dikenal untuk ini adalah dengan memanggil shell:
Dalam hal ini, pengalihan (
>
) dikutip pada shell sekarang dan bekerja dengan benar di dalam shell yang disebut. Inicalled_shell
digunakan sebagai$0
parameter (nama) dari shell anak (sh
).Itu bekerja dengan baik jika sufiks ditambahkan nama file, tetapi tidak jika Anda menggunakan awalan. Agar awalan berfungsi, Anda harus menghapus
./
yang ditemukan sebelumnya dengan nama file${1#./}
dan menggunakan-execdir
opsi.Anda mungkin (atau mungkin tidak) ingin menggunakan
-iname
opsi sehingga file yang bernama*.JPG
atau*.JpG
variasi lain juga disertakan.Dan, Anda mungkin (atau mungkin tidak) juga ingin memanggil shell sekali per direktori alih-alih sekali per file dengan menambahkan loop (
for f do … ; done
) dan a+
di akhir:Dan, akhirnya, karena
cjpeg
dapat langsung menulis ke file, pengalihan dapat dihindari sebagai:sumber
cjpeg
memiliki opsi yang memungkinkan Anda menulis ke file bernama, daripada output standar. Jika versi Andafind
mendukung-execdir
opsi, Anda dapat memanfaatkannya untuk membuat pengalihan tidak perlu.Catatan: ini benar-benar mengasumsikan versi BSD
find
, yang muncul untuk menghapus yang terdepan./
dari nama file saat exanding{}
. (Atau sebaliknya, GNUfind
menambahkan./
namanya. Tidak ada standar untuk mengatakan perilaku mana yang "benar".)sumber
find
mendukung-execdir
, Anda bisa menggunakannya-exec
. Ini menyebabkan perintah dijalankan di direktori tempat file ditemukan, dan{}
akan menjadiaa.jpg
bukan./t2/aa.jpg
.cjpeg: can't open optimized_./aa.jpg
.find
dan BSDfind
. (Bahaya menggunakan ekstensi non-standar.)./
tampaknya lebih rentan terhadap kesalahan. Solusi yang masuk akal diusulkan dalam jawaban ini .Buat skrip cjq80:
Jadikan itu dapat dieksekusi
Dan gunakan di -exec:
sumber