bash - bisakah saya melakukannya: temukan ... -exec this && that?


23

Apakah ada cara untuk secara logis menggabungkan dua perintah shell yang dipanggil dengan find - exec ?

Sebagai contoh untuk mencetak semua file .csv yang berisi string foo bersama dengan kejadiannya, saya ingin melakukan:

find . -iname \*.csv -exec grep foo {} && echo {} \;

tapi bash mengeluh dengan "argumen yang hilang ke '-exec'"


2
Anda dapat menggunakan 2 -execsecara berurutan atau menggunakan satu -exec sh -c 'grep foo "$0" && printf %s\\n "$0"' {} \;.
jw013

Ini telah membuat saya tersandung berulang kali: Saya selalu berharap bahwa argumen pertama yang diteruskan ke sh(dalam hal ini {}) akan $1dan $0akan menjadi sesuatu seperti sh. Tetapi pada kenyataannya, Anda benar, argumen pertama muncul sebagai $0. Memiliki argumen pertama menjadi nama perintah pemanggilan hanyalah sebuah konvensi, yang tidak secara otomatis ditegakkan dalam kasus ini.
dubiousjim

Mungkin harus digabung dengan pertanyaan ini: unix.stackexchange.com/q/18077/4801
dubiousjim

Jawaban:


24

-execadalah predikat yang menjalankan perintah (bukan shell) dan mengevaluasi true atau false berdasarkan hasil dari perintah (status keluar nol atau tidak nol).

Begitu:

find . -iname '*.csv' -exec grep foo {} \; -print

akan mencetak path file jika grepmenemukan foo dalam file. Alih-alih -printAnda dapat menggunakan -execpredikat lain atau predikat lain

find . -iname '*.csv' -exec grep foo {} \; -exec echo {} \;

Lihat juga !dan -otemukan operator untuk negasi dan atau .

Atau, Anda dapat memulai shell sebagai:

find . -iname '*.csv' -exec sh -c '
   grep foo "$1" && echo "$1"' sh {} \;

Atau untuk menghindari keharusan memulai shell untuk setiap file:

find . -iname '*.csv' -exec sh -c '
  for i do
    grep foo "$i" && echo "$i"
  done' sh {} +

10

Masalah yang Anda hadapi adalah shell pertama-tama mem-parsing baris perintah, dan melihat dua perintah sederhana yang dipisahkan oleh &&operator:, find . -iname \*.csv -exec grep foo {}dan echo {} \;. Mengutip &&( find . -iname \*.csv -exec grep foo {} '&&' echo {} \;) bypasses itu, tapi sekarang perintah dieksekusi oleh findadalah sesuatu seperti grepdengan argumen foo, wibble.csv, &&, echodan wibble.csv. Anda perlu menginstruksikan findmenjalankan shell yang akan menafsirkan &&operator:

find . -iname \*.csv -exec sh -c 'grep foo "$0" && echo "$0"' {} \;

Perhatikan bahwa argumen pertama setelahnya sh -c SOMECOMMANDadalah $0, bukan $1.

Anda dapat menghemat waktu startup dari proses shell untuk setiap file dengan mengelompokkan doa perintah dengan -exec … +. Untuk memudahkan pemrosesan, berikan beberapa nilai dummy $0sehingga "$@"menyebutkan nama file.

find . -iname \*.csv -exec sh -c 'for x in "$@"; do grep foo "$x" && echo "$x"; done' \ {} +

Jika perintah shell hanya dipisahkan oleh dua program &&, finddapat melakukan pekerjaan itu sendiri: tulis dua -exectindakan berurutan , dan yang kedua hanya akan dieksekusi jika yang pertama keluar dengan status 0.

find . -iname \*.csv -exec grep foo {} \; -exec echo {} \;

(Saya berasumsi itu grepdan echohanya untuk tujuan ilustrasi, karena -exec echodapat digantikan oleh -printdan output yang dihasilkan tidak terlalu berguna.)


2
Saya cenderung menghindari menggunakan "$ 0" untuk itu, karena ini juga digunakan oleh shell untuk menampilkan pesan kesalahan. Misalnya, Anda dapat melihat ./some-file: grep: command not foundpesan kesalahan yang membingungkan . -exec find sh -c '... "$1"' sh {} \;tidak akan memiliki masalah. Ada kesalahan ketik (terkait) di perintah find kedua Anda.
Stéphane Chazelas

3

Dalam kasus khusus ini saya akan lakukan:

find . -iname \*.csv -exec grep -l foo \{\} \;

Atau jika Anda memiliki ACK :

ack -al -G '.*\.csv' foo

Untuk menjawab pertanyaan Anda yang sebenarnya, sesuatu seperti ini dapat bekerja:

find . -iname \*.csv -exec sh -c "grep foo {} && echo {}" \;


3
Yang terakhir menemukan perintah tidak hanya non-portabel tetapi juga sangat berbahaya karena jalur file akhirnya dievaluasi sebagai kode shell.
Stéphane Chazelas

Semua alasan lagi untuk mencoba dan menghindarinya dengan menggunakan ack / grep dengan benar :)
Dennis Kaarsemaker

1
Alternatif jw013 (diberikan dalam komentar untuk pertanyaan) lebih aman dalam hal ini. (Hanya memperhatikan bahwa jawaban Gilles dan Stephane menggunakan teknik yang sama.)
dubiousjim
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.