Jawaban ini datang di bagian berikut:
- Penggunaan dasar
-exec
- Menggunakan
-exec
dalam kombinasi dengansh -c
- Menggunakan
-exec ... {} +
- Menggunakan
-execdir
Penggunaan dasar -exec
The -exec
pilihan mengambil utilitas eksternal dengan argumen opsional sebagai argumen dan mengeksekusi itu.
Jika string {}
ada di mana saja dalam perintah yang diberikan, setiap instance akan digantikan oleh pathname yang saat ini sedang diproses (misalnya ./some/path/FILENAME
). Dalam kebanyakan shell, kedua karakter {}
tidak perlu dikutip.
Perintah perlu diakhiri dengan ;
untuk find
mengetahui di mana ia berakhir (karena mungkin ada opsi lebih lanjut setelah itu). Untuk melindungi ;
shell, shell harus dikutip sebagai \;
atau ';'
, jika tidak shell akan melihatnya sebagai akhir dari find
perintah.
Contoh ( \
pada akhir dua baris pertama hanya untuk kelanjutan baris):
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
Ini akan menemukan semua file biasa ( -type f
) yang namanya cocok dengan pola *.txt
di atau di bawah direktori saat ini. Kemudian akan menguji apakah string hello
terjadi di salah satu file yang ditemukan menggunakan grep -q
(yang tidak menghasilkan output apa pun, hanya status keluar). Untuk file-file yang berisi string, cat
akan dieksekusi untuk menampilkan isi file ke terminal.
Masing-masing -exec
juga bertindak seperti "tes" pada nama path yang ditemukan oleh find
, sama seperti -type
dan -name
tidak. Jika perintah mengembalikan status keluar nol (menandakan "sukses"), bagian selanjutnya dari find
perintah dipertimbangkan, jika tidak, find
perintah akan dilanjutkan dengan nama path berikutnya. Ini digunakan dalam contoh di atas untuk menemukan file yang berisi string hello
, tetapi untuk mengabaikan semua file lainnya.
Contoh di atas menggambarkan dua kasus penggunaan paling umum -exec
:
- Sebagai ujian untuk lebih membatasi pencarian.
- Untuk melakukan beberapa tindakan pada pathname yang ditemukan (biasanya, tetapi tidak harus, di akhir
find
perintah).
Menggunakan -exec
dalam kombinasi dengansh -c
Perintah yang -exec
dapat dieksekusi terbatas pada utilitas eksternal dengan argumen opsional. Untuk menggunakan built-in shell, fungsi, kondisional, jalur pipa, pengalihan dll secara langsung dengan -exec
tidak dimungkinkan, kecuali dibungkus dengan sesuatu seperti sh -c
shell anak.
Jika bash
fitur diperlukan, maka gunakan bash -c
di tempat sh -c
.
sh -c
berjalan /bin/sh
dengan skrip yang diberikan pada baris perintah, diikuti oleh argumen baris perintah opsional untuk skrip itu.
Contoh sederhana penggunaannya sh -c
sendiri, tanpa find
:
sh -c 'echo "You gave me $1, thanks!"' sh "apples"
Ini meneruskan dua argumen ke skrip shell anak:
String sh
. Ini akan tersedia sebagai $0
di dalam skrip, dan jika shell internal menampilkan pesan kesalahan, ia akan awalan dengan string ini.
Argumen apples
tersebut tersedia seperti $1
dalam skrip, dan jika ada lebih banyak argumen, maka ini akan tersedia sebagai $2
, $3
dll. Mereka juga akan tersedia dalam daftar "$@"
(kecuali $0
yang tidak akan menjadi bagian dari "$@"
).
Ini berguna dalam kombinasi dengan -exec
karena memungkinkan kita untuk membuat skrip kompleks yang bertindak sewenang-wenang pada nama path yang ditemukan oleh find
.
Contoh: Temukan semua file biasa yang memiliki akhiran nama file tertentu, dan ubah akhiran nama file itu ke beberapa akhiran lainnya, di mana akhiran disimpan dalam variabel:
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c 'mv "$3" "${3%.$1}.$2"' sh "$from" "$to" {} ';'
Di dalam skrip internal, $1
akan menjadi string text
, $2
akan menjadi string txt
dan $3
akan menjadi apa pun pathname yang find
ditemukan untuk kita. Ekspansi parameter ${3%.$1}
akan mengambil pathname dan menghapus akhiran .text
darinya.
Atau, menggunakan dirname
/ basename
:
find . -type f -name "*.$from" -exec sh -c '
mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"' sh "$from" "$to" {} ';'
atau, dengan variabel yang ditambahkan di skrip internal:
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2; pathname=$3
mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"' sh "$from" "$to" {} ';'
Perhatikan bahwa dalam variasi terakhir ini, variabel from
dan to
di shell anak berbeda dari variabel dengan nama yang sama di skrip eksternal.
Di atas adalah cara yang benar memanggil script kompleks sewenang-wenang dari -exec
dengan find
. Menggunakan find
dalam lingkaran seperti
for pathname in $( find ... ); do
rentan terhadap kesalahan dan tidak tangguh (pendapat pribadi). Ini adalah pemisahan nama file pada spasi putih, memanggil globbing nama file, dan juga memaksa shell untuk memperluas hasil lengkap find
sebelum bahkan menjalankan iterasi pertama dari loop.
Lihat juga:
Menggunakan -exec ... {} +
Pada ;
akhirnya dapat diganti dengan +
. Ini menyebabkan find
untuk mengeksekusi perintah yang diberikan dengan sebanyak mungkin argumen (ditemukan nama path) daripada satu untuk setiap nama path yang ditemukan. String {}
harus terjadi tepat sebelum +
ini berfungsi .
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} +
Di sini, find
akan mengumpulkan nama path yang dihasilkan dan mengeksekusi cat
pada sebanyak mungkin dari mereka sekaligus.
find . -type f -name "*.txt" \
-exec grep -q "hello" {} ';' \
-exec mv -t /tmp/files_with_hello/ {} +
Demikian juga di sini, mv
akan dieksekusi beberapa kali mungkin. Contoh terakhir ini membutuhkan GNU mv
dari coreutils (yang mendukung -t
opsi).
Menggunakan -exec sh -c ... {} +
juga merupakan cara yang efisien untuk mengulang set nama path dengan skrip yang sewenang-wenang.
Dasar-dasarnya sama dengan ketika menggunakan -exec sh -c ... {} ';'
, tetapi skrip sekarang membutuhkan daftar argumen yang jauh lebih lama. Ini dapat diulang dengan mengulang "$@"
di dalam skrip.
Contoh kami dari bagian terakhir yang mengubah akhiran nama file:
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2
shift 2 # remove the first two arguments from the list
# because in this case these are *not* pathnames
# given to us by find
for pathname do # or: for pathname in "$@"; do
mv "$pathname" "${pathname%.$from}.$to"
done' sh "$from" "$to" {} +
Menggunakan -execdir
Ada juga -execdir
(diimplementasikan oleh sebagian besar find
varian, tetapi bukan opsi standar).
Ini berfungsi seperti -exec
dengan perbedaan bahwa perintah shell yang diberikan dieksekusi dengan direktori pathname yang ditemukan sebagai direktori kerjanya saat ini dan itu {}
akan berisi nama samaran dari pathname yang ditemukan tanpa pathnya (tetapi GNU find
masih akan awalan dengan nama basenya ./
, sementara BSD find
tidak akan melakukan itu).
Contoh:
find . -type f -name '*.txt' \
-execdir mv {} done-texts/{}.done \;
Ini akan memindahkan setiap *.txt
file -file yang ditemukan ke done-texts
subdirektori yang sudah ada di direktori yang sama dengan tempat file ditemukan . File juga akan diganti namanya dengan menambahkan akhiran .done
ke dalamnya.
Ini akan sedikit lebih sulit untuk dilakukan -exec
karena kita harus mengeluarkan nama file dari file yang ditemukan {}
untuk membentuk nama file yang baru. Kami juga memerlukan nama direktori dari {}
untuk menemukan done-texts
direktori dengan benar.
Dengan -execdir
, beberapa hal seperti ini menjadi lebih mudah.
Operasi yang sesuai menggunakan -exec
alih-alih -execdir
harus menggunakan shell anak:
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done"
done' sh {} +
atau,
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "${name%/*}/done-texts/${name##*/}.done"
done' sh {} +