Menggunakan titik koma (;) vs plus (+) dengan exec in find

159

Mengapa ada perbedaan dalam output antara menggunakan

find . -exec ls '{}' \+

dan

find . -exec ls '{}' \;

Saya mendapatkan:

$ find . -exec ls  \{\} \+
./file1  ./file2

.:
file1  file2  testdir1

./testdir1:
testdir2

./testdir1/testdir2:


$ find . -exec ls  \{\} \;
file1  file2  testdir1
testdir2
./file2
./file1
Ankur Agarwal
sumber

Jawaban:

249

Ini mungkin digambarkan dengan contoh. Katakanlah findmuncul file-file ini:

file1
file2
file3

Menggunakan -execdengan tanda titik koma ( find . -exec ls '{}' \;), akan mengeksekusi

ls file1
ls file2
ls file3

Tetapi jika Anda menggunakan tanda tambah sebagai gantinya ( find . -exec ls '{}' \+), sebanyak mungkin nama file dilewatkan sebagai argumen untuk satu perintah:

ls file1 file2 file3

Jumlah nama file hanya dibatasi oleh panjang baris perintah maksimum sistem. Jika perintah melebihi panjang ini, perintah akan dipanggil beberapa kali.

Martin
sumber
1
Terima kasih. ini sangat berguna untuk ingin mengurutkan file yang dihasilkan: find -maxdepth 1 -type f -mtime -1 -exec ls -ltr {} \ +
fbas
1
Bodoh q: Saya perhatikan bahwa +terkait dengan -execselalu lolos, tetapi +terkait dengan -mtimetidak. Apakah Anda tahu alasannya? Saya kira itu adalah kebiasaan untuk tidak ;berhubungan dengan -exec.
kevinarpe
3
@ kevinarpe memang, saya akan menggunakan kebiasaan itu ;. Tidak dapat membayangkan perlunya melarikan diri+
Martin
36

Semua jawaban sejauh ini benar. Saya menawarkan ini sebagai demonstrasi yang lebih jelas (kepada saya) dari perilaku yang digambarkan menggunakan echodaripada ls:

Dengan titik koma, perintah echoini disebut sekali per file (atau objek sistem file lain) yang ditemukan:

$ find . -name 'test*' -exec echo {} \;
./test.c
./test.cpp
./test.new
./test.php
./test.py
./test.sh

Dengan nilai tambah, perintah echoini dipanggil sekali saja. Setiap file yang ditemukan diteruskan sebagai argumen.

$ find . -name 'test*' -exec echo {} \+
./test.c ./test.cpp ./test.new ./test.php ./test.py ./test.sh

Jika findmenghasilkan sejumlah besar hasil, Anda mungkin menemukan bahwa perintah yang dipanggil tersedak pada sejumlah argumen.

Johnsyweb
sumber
1
Bukankah seharusnya menambahkan hasil hanya ke nomor yang membuat aman untuk meneruskannya ke shell? Setidaknya itulah yang xargsdilakukan ... pada prinsipnya tidak pernah tersedak karena terlalu banyak argumen.
Rmano
1
@Rmano: Saya telah melihat find(dan xargs) di Solaris memancarkan lebih banyak argumen daripada yang bisa dikonsumsi. Tanda xargs(dan find) dalam findutils GNU tampaknya berperilaku lebih masuk akal, tetapi tidak semua orang menggunakan GNU.
Johnsyweb
2
@Johnsyweb, semua POSIX findakan mencoba untuk menghindari mencapai batas jumlah argumen. Dan itu termasuk Solaris (setidaknya 10). Di mana itu mungkin gagal adalah jika Anda melakukan sesuatu seperti find ... -exec ksh -c 'cmd "$@" "$@"' sh {} +atau find ... -exec ksh -c 'files="$*" cmd "$@"' sh {} +, tetapi findtidak dapat benar-benar disalahkan untuk itu. Perhatikan bahwa GNU findadalah salah satu findimplementasi terakhir yang didukung +(dulu merupakan skrip port ke sistem GNU yang menyakitkan).
Stephane Chazelas
19

Dari man find:

perintah -exec;

Jalankan perintah; true jika 0 status dikembalikan. Semua argumen berikut untuk ditemukan dianggap sebagai argumen untuk perintah sampai argumen yang terdiri dari ';' ditemui. String '{}' diganti oleh nama file saat ini sedang diproses di mana-mana itu terjadi dalam argumen ke perintah, tidak hanya dalam argumen di mana itu sendirian, seperti dalam beberapa versi find. Kedua konstruksi ini mungkin perlu diloloskan (dengan tanda '\') atau dikutip untuk melindunginya dari ekspansi oleh shell. Lihat bagian EXAMPLES dtk untuk contoh penggunaan opsi '-exec'. Perintah yang ditentukan dijalankan sekali untuk setiap file yang cocok. Perintah dijalankan di direktori awal. Ada masalah keamanan yang tidak dapat dihindari seputar penggunaan opsi -exec;

perintah -exec {} +

Varian opsi -exec ini menjalankan perintah yang ditentukan pada file yang dipilih, tetapi baris perintah dibangun dengan menambahkan setiap nama file yang dipilih di akhir ; jumlah total doa perintah akan jauh lebih sedikit daripada jumlah file yang cocok. Baris perintah dibangun dengan cara yang sama seperti xargs membangun baris perintahnya. Hanya satu instance dari '{}' diizinkan dalam perintah. Perintah dijalankan di direktori awal.

Jadi, seperti yang saya pahami, \;menjalankan perintah terpisah untuk setiap file yang ditemukan oleh find, sedangkan \+menambahkan file dan mengeksekusi satu perintah pada mereka semua. Ini \adalah karakter pelarian, jadi:

ls testdir1; ls testdir2 

vs.

ls testdir1 testdir2

Melakukan hal di atas di shell saya mencerminkan output dalam pertanyaan Anda.

contoh kapan Anda ingin menggunakannya \+

Misalkan dua file, 1.tmpdan2.tmp :

1.tmp:

1
2
3

2.tmp:

0
2
3

Dengan \;:

 find *.tmp -exec diff {} \;
> diff: missing operand after `1.tmp'
> diff: Try `diff --help' for more information.
> diff: missing operand after `2.tmp'
> diff: Try `diff --help' for more information.

Sedangkan jika Anda menggunakan \+(untuk menggabungkan hasil find):

find *.tmp -exec diff {} \+
1c1,3
< 1
---
> 0
> 2
> 30

Jadi dalam hal ini bedanya diff 1.tmp; diff 2.tmp dandiff 1.tmp 2.tmp

Ada kasus di mana \;sesuai dan \+akan diperlukan. Menggunakan \+dengan rmadalah salah satu contohnya, di mana jika Anda menghapus sejumlah besar kinerja file (kecepatan) akan lebih unggul \;.

cocok
sumber
Saya dapat membaca halaman manual juga. Dan saya lakukan, tapi saya rasa saya tidak mengerti perbedaan antara menggunakan; vs +
Ankur Agarwal
Saya tidak berpikir -1 itu adil, saya menjelaskan pengertian saya tentang pria itu. Saya tidak hanya menyalin pria itu dan pergi. tetapi saya telah mengedit respons saya untuk memasukkan contoh yang lebih baik.
cocok
10

findmemiliki sintaks khusus. Anda menggunakan {}apa adanya karena mereka memiliki makna untuk menemukan sebagai pathname dari file yang ditemukan dan (kebanyakan) shell tidak menafsirkannya sebaliknya. Anda perlu garis miring terbalik \;karena tanda koma memiliki makna pada shell, yang memakannya sebelum findbisa mendapatkannya. Jadi apa yang findingin dilihat SETELAH shell selesai, dalam daftar argumen yang diteruskan ke program C, adalah

"-exec", "rm", "{}", ";"

tetapi Anda perlu \;pada baris perintah untuk mendapatkan titik koma melalui shell ke argumen.

Anda dapat lolos \{\}karena interpretasi shell-dikutip \{\}adalah adil {}. Demikian pula, Anda dapat menggunakan '{}'.

Apa yang tidak bisa Anda lakukan adalah menggunakan

 -exec 'rm {} ;'

karena shell mengartikan itu sebagai satu argumen,

"-exec", "rm {};"

dan rm {} ;bukan nama perintah. (Setidaknya kecuali seseorang benar-benar main-main.)

Memperbarui

perbedaannya adalah antara

$ ls file1
$ ls file2

dan

$ ls file1 file2

The +catenating nama ke baris perintah.

Charlie Martin
sumber
1
Saya mengerti apa yang Anda katakan. Saya bertanya apa perbedaan antara menggunakan; vs +
Ankur Agarwal
1
maaf, tetapi apakah Anda membaca pertanyaan saya atau komentar saya dengan hati-hati? Mungkin saya perlu mengulanginya. Mengapa ada o / p yang berbeda ketika saya menggunakan titik koma dengan exec in find versus ketika saya menggunakan plus dengan exec in find?
Ankur Agarwal
2
Ini adalah penjelasan yang bagus untuk MENGAPA perintahnya seperti ini, bahwa jawaban yang diterima tidak mencakup. Terima kasih!
Sherwin Yu
1

Perbedaan antara ;(titik koma) atau +(tanda tambah) adalah bagaimana argumen diteruskan ke find's -exec/ -execdirparameter. Sebagai contoh:

  • menggunakan ;akan menjalankan banyak perintah (secara terpisah untuk setiap argumen),

    Contoh:

    $ find /etc/rc* -exec echo Arg: {} ';'
    Arg: /etc/rc.common
    Arg: /etc/rc.common~previous
    Arg: /etc/rc.local
    Arg: /etc/rc.netboot
    

    Semua argumen berikut untuk find dianggap sebagai argumen untuk perintah.

    String {}diganti oleh nama file saat ini sedang diproses.

  • menggunakan +akan mengeksekusi perintah sekecil mungkin (karena argumen digabungkan bersama). Ini sangat mirip dengan cara xargskerja perintah, sehingga akan menggunakan argumen sebanyak mungkin per perintah untuk menghindari melebihi batas maksimum argumen per baris.

    Contoh:

    $ find /etc/rc* -exec echo Arg: {} '+'
    Arg: /etc/rc.common /etc/rc.common~previous /etc/rc.local /etc/rc.netboot
    

    Baris perintah dibangun dengan menambahkan setiap nama file yang dipilih di akhir.

    Hanya satu instance dari {}yang diizinkan dalam perintah.

Lihat juga:

kenorb
sumber
-5

kami mencoba mencari file untuk tata graha.

Temukan . -exec echo {} \; perintah dijalankan pada malam hari pada akhirnya tidak ada hasil.

Temukan . -exec echo {} \ + memiliki hasil dan hanya memakan waktu beberapa jam.

Semoga ini membantu.

Ron
sumber
2
Jawaban ini tidak menjelaskan bagaimana kedua cara itu bekerja dan bagaimana hasil yang dihasilkannya berbeda.
misko321