GNU menemukan dan menutupi {} untuk beberapa shell - yang mana?

34

Halaman manual untuk GNU find state:

-exec command ;
    [...] The  string `{}'  is  replaced  by the current 
    file name being processed everywhere it occurs in the 
    arguments to the command, not just in arguments where 
    it is alone, as in some  versions  of  find.
    Both  of  these  constructions might need to be escaped 
    (with a `\') or quoted to protect them from expansion 
    by the shell. 

Itu dari man to find(GNU findutils) 4.4.2.

Sekarang saya menguji ini dengan bash dan dash, dan keduanya tidak perlu memiliki {}topeng. Berikut ini adalah tes sederhana:

find /etc -name "hosts" -exec md5sum {} \; 

Apakah ada shell, yang saya benar-benar perlu untuk menutupi kawat gigi? Perhatikan, itu tidak tergantung pada apakah file yang ditemukan berisi kosong (dipanggil dari bash):

find ~ -maxdepth 1 -type d -name "U*" -exec ls -d {} \; 
/home/stefan/Ubuntu One

Ini berubah jika file yang ditemukan diteruskan ke subkulit:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d {}' \; 
ls: cannot access /home/stefan/Ubuntu: No such file or directory
ls: cannot access One: No such file or directory

yang dapat diselesaikan dengan:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "$0"' {} \;

berlawanan dengan:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "{}"' \; 
/home/stefan/Ubuntu One

tapi bukan itu yang dibicarakan oleh halaman manual, bukan? Jadi cangkang mana yang memperlakukan {}dengan cara yang berbeda?

Pengguna tidak diketahui
sumber
@Volker Siegel: Suntingan Anda mungkin terjadi dengan niat baik, tetapi saya memeriksa manual-cari saya saat ini dan koreksi Anda salah, Anda juga melewatkan kesempatan untuk meringkas hasil edit Anda yang merupakan kebiasaan buruk, Jadi saya memutar kembali perubahan Anda. Maaf teman
pengguna tidak diketahui
Oh, terima kasih sudah mengembalikannya jika itu merusak sesuatu. Saya ingat bahwa kompiler teks yang menghasilkan format halaman manual yang digunakan untuk menghasilkan "kutipan tipografi" ini sebagai artefak dari definisi rendering. Itu juga dapat merender ke TeX untuk format cetakan yang sempurna. Saya perhatikan tanda kutip karena mereka membingungkan penyorotan sintaksis. Saya berasumsi bahwa kutipan pertama yang tidak biasa salah ketika digunakan dalam kode, dan masih menganggap itu. Perhatikan bahwa kedua perubahan itu dalam teks deskripsi, bukan kode. Jadi jika kita melihatnya sebagai tipografi - render estetika - mereka benar.
Volker Siegel
Sepertinya dalam rendering keluaran info, kutipan yang sama digunakan di kedua sisi. Tidak ada keraguan perubahan saya membuatnya berbeda dari yang asli, Anda benar. Tetapi apakah itu menyebabkan masalah? (Saya menganggap kutipan pertama sebagai bug dalam definisi rendering man, itu satu-satunya kasus tipografi sejauh yang saya tahu.)
Volker Siegel
Saya baru saja memeriksa: `{} 'tidak berfungsi di baris perintah. Secara teknis itu benar, tetapi saya tidak suka bahwa pengguna yang tidak curiga mendapat kesalahan aneh ketika ia mencoba menyalin dan menempelnya.
Volker Siegel
@ VolkerSiegel: Ini adalah teks prosa, bukan kode, jadi apa yang harus pengguna salin-tempel di sini? Dan ini adalah kutipan dari halaman manual dari 2011. Saya tidak melihat ada nilai untuk memperbaiki bagian tersebut untuk kemudian menunjukkan, mengapa saya membuat koreksi ke halaman manual. Topiknya cukup rumit. Jika ini pertanyaan Anda, saya tidak akan mencoba meyakinkan Anda, untuk mengutip halaman manual secara akurat tetapi untuk pertanyaan saya, saya tidak berminat melakukan kompromi. Kutipan adalah kutipan.
pengguna tidak diketahui

Jawaban:

26

Rangkuman : Jika pernah ada shell yang mengembang {}, itu benar-benar barang lawas sekarang.

Dalam cangkang Bourne dan cangkang yang sesuai dengan POSIX, kurung kurawal ( {dan }) adalah karakter biasa (tidak seperti (dan )yang merupakan pembatas kata seperti ;dan &, dan [dan ]yang merupakan penggumpalan karakter). Semua string berikut seharusnya dicetak secara literal:

$ echo { } {} {foo,bar} {1..3}
{ } {} {foo,bar} {1..3}

Sebuah kata yang terdiri dari penjepit tunggal adalah kata yang dipesan , yang hanya khusus jika itu adalah kata pertama dari suatu perintah.

Ksh mengimplementasikan ekspansi brace sebagai ekstensi yang tidak kompatibel ke shell Bourne. Ini bisa dimatikan dengan set +B. Bash mengemulasi ksh dalam hal ini. Zsh mengimplementasikan ekspansi brace juga; ada itu dapat dimatikan dengan set +Iatau setopt ignore_bracesatau emulate sh. Tak satu pun dari cangkang ini mengembang {}dalam hal apa pun, bahkan ketika itu adalah substring dari sebuah kata (misalnya foo{}bar), karena penggunaan umum dalam argumen ke finddan xargs.

Single Unix v2 mencatat itu

Dalam beberapa sistem historis, kurung kurawal diperlakukan sebagai operator kontrol. Untuk membantu dalam kegiatan standardisasi di masa depan, aplikasi portabel harus menghindari penggunaan tanda kurung tanda kutip untuk mewakili karakter itu sendiri. Ada kemungkinan bahwa versi masa depan dari standar ISO / IEC 9945-2: 1993 mungkin mengharuskan {dan }diperlakukan secara individual sebagai operator kontrol, meskipun token {}mungkin akan menjadi pengecualian kasus khusus dari ini karena find {}konstruk yang sering digunakan .

Catatan ini dijatuhkan dalam versi standar berikutnya; yang contoh untukfind memiliki kegunaan kuotasi dari {}, seperti melakukan contoh untukxargs . Mungkin ada kerang Bourne historis di mana {}harus dikutip, tetapi mereka akan menjadi sistem warisan yang benar-benar tua sekarang.

Implementasi csh saya harus di tangan (OpenBSD 4.7, BSD csh pada Debian , tcsh) semua memperluas {foo}ke footetapi meninggalkan {}sendirian.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
@geekosaur: Hanya sebagian kecil penurunan harga berfungsi di komentar . Saya tidak tahu apa yang ingin Anda katakan. Jika ini tentang ekspansi brace ksh et al, bacalah paragraf saya yang dimulai dengan “Ksh mengimplementasikan ekspansi brace sebagai ekstensi yang tidak kompatibel”.
Gilles 'SO- stop being evil'
2
Itu bash(lihat $BASH_VERSION). Ekspansi brace sangat hidup dan sehat.
geekosaur
2
Itu adalah titik. The {}sintaks berasal csh, tetapi {}diperluas ke string kosong. Kerang yang lebih baru mengakui bahwa itu tidak masuk akal, tetapi masih ada beberapa yang lama cshdi luar sana.
geekosaur
1
@geekosaur: Bisakah Anda mengatakan versi csh spesifik pada platform spesifik mana? Saya lebih suka mengujinya sendiri (jika memungkinkan di linux) atau yakinlah, bahwa orang itu mengujinya sendiri.
pengguna tidak diketahui
1
@geekosaur, {}fooakan memperluas ke foo, tetapi {}akan memperluas ke {}(kecuali ketika dalam backticks, tetapi mengutip tidak akan membantu) dan didokumentasikan seperti itu. Saya memverifikasi untuk csh dari 2BSD (rilis pertama), 2.79BSD, 2.8BSD dan 2.11BSD.
Stéphane Chazelas
12

Yang {}perlu dikutip dalam versi fishshell sebelum 3.0.0.

$ fish -c 'echo find -exec {} \;'
find -exec  ;

Dan di shell rc (juga akangadidasarkan pada rc, tetapi tidak es):

$ rc -c "echo find -exec {} ';'"
line 1: syntax error near '{'

Itu mungkin bukan shell yang penulis GNU temukan dalam pikiran ketika mereka menulis teks fishitu sejak pertama kali dirilis pada 2005 (sementara teks atau sejenisnya sudah ada pada 1994) dan rcpada awalnya bukan shell Unix.

Ada beberapa rumor tentang beberapa versi csh(shell yang memperkenalkan brace expansion) yang membutuhkannya. Tetapi sulit untuk memberikan kredit kepada mereka sejak rilis pertama cshdi 2BSD tidak. Di sini seperti yang diuji dalam emulator PDP11:

# echo find -exec {} \;
find -exec {} ;

Dan halaman manual dari 2BSD cshdengan jelas menyatakan :

Sebagai kasus khusus `{',`}' dan `{} 'diteruskan tanpa gangguan.

Jadi saya akan merasa sangat aneh jika versi selanjutnya dari csh atau tcsh kemudian memecahkannya.

Bisa jadi untuk mengatasi beberapa bug dalam beberapa versi. Masih dengan 2BSD csh (sama dengan 2.79BSD, 2.8BSD, 2.11BSD):

# csh -x
# echo foo {} bar
echo foo {} bar
foo {} bar
# echo `echo foo {} bar`
echo `echo foo {} bar`
echo foo {} bar
foo  bar

Mengutip tidak membantu:

# echo `echo foo '{}' bar`
echo `echo foo '{}' bar`
echo foo {} bar
foo  bar

Anda bisa mengutip seluruh substitusi perintah:

# echo "`echo foo {} bar`"
echo `echo foo {} bar`
echo foo {} bar
foo {} bar

Tapi itu melewati satu argumen ke gema luar itu.

Di cshatau tcsh, Anda harus mengutip {}ketika tidak sendiri seperti di:

find . -name '*.txt' -type f -exec cp {} '{}.back' \;

(Meskipun findpenggunaan semacam itu tidak portabel karena beberapa finds hanya berkembang {}ketika sendiri).

Stéphane Chazelas
sumber
1

Dalam satu kata csh,. bashdan shell modern lainnya mengakui bahwa pengguna mungkin tidak meminta ekspansi penjepit nol. (Modern cshsebenarnya tcshdan mungkin juga menangani {}dengan waras sekarang.)

geekosaurus
sumber
Senang mendengar, tetapi saya meminta yang sebaliknya, sebuah shell yang tidak bisa menanganinya dengan baik.
pengguna tidak diketahui
Anda berasumsi bahwa seseorang akan masuk dan merobek referensi halaman info untuk mengutip tambahan hanya karena sistem Linux semua memiliki yang lebih baru csh. Tidak semua orang menjalankan utilitas GNU di Linux; sebenarnya itu cukup umum untuk menginstalnya pada sistem Unix komersial yang lebih tua yang perintah bundelnya terbatas. (Mengganti cshsistem-sistem itu lebih kecil kemungkinannya, karena skrip-skrip sistem mungkin mengandalkan keanehan dari dokumen asli csh, dan bahkan jika semua pengguna dikonfigurasikan untuk menggunakan yang lebih baru csh, roothampir pasti harus tetap menggunakan versi yang dibundel.)
geekosaur
2
Tidak. Saya sedang berdebat dengan seorang pria yang mengatakan, bahwa dalam wiki kami, kami harus memberikan saran untuk menggunakan "{}" setiap saat, karena halaman manual mengatakan demikian. Dan sekarang saya ingin tahu apakah ada shell yang tidak saya gunakan, seperti ksh, zsh, tcsh, csh atau XYZsh yang saya tidak tahu, yang mana klaim ini benar, atau apakah saya dapat dengan jujur ​​berasumsi, bahwa tidak ada. Jika ada cangkang untuk berbagai Unix yang memerlukan "{}", itu akan menjadi penjelasan yang bagus mengapa kalimat tersebut masih ada di halaman manual, tetapi tidak sesuai sebagai saran untuk pemula linux.
pengguna tidak diketahui
Sekarang saya bertanya-tanya apakah pdkshmelakukan hal yang benar ... walaupun jawaban yang benar untuk itu mungkin "nyata kshadalah FOSS hari ini".
geekosaur
4
Pdksh, seperti mksh, ksh93, bash dan zsh, hanya memperluas kawat gigi ketika ada koma di antaranya (atau ..untuk ksh93, bash dan zsh). Hanya (t) csh mengembang {foo}ke foo, dan bahkan ia meninggalkan {}saja (setidaknya pada baru-baru BSD).
Gilles 'SANGAT berhenti menjadi jahat'