Bagaimana tanda kutip ganda dalam bash cocok (berpasangan)?


15

Saya menggunakan GNU bash 4.3.48. Pertimbangkan dua perintah berikut yang hanya berbeda dengan satu tanda dolar.

Perintah 1:

echo "(echo " * ")"

Perintah 2:

echo "$(echo " * ")"

Outputnya masing-masing

(echo  test.txt ppcg.sh )

dan

 * 

Jadi jelas dalam kasus pertama *adalah globbed, yang berarti bahwa tanda kutip pertama pergi dengan yang kedua untuk membentuk pasangan, dan yang ketiga dan keempat membentuk pasangan yang lain.

Dalam kasus kedua *ini tidak menggumpal, dan ada tepat dua ruang ekstra dalam output, satu sebelum asterisk dan satu setelah, yang berarti bahwa tanda kutip kedua berjalan dengan yang ketiga, dan yang pertama pergi dengan yang keempat.

Apakah ada kasus lain selain $()konstruksi yang tanda kutipnya tidak cocok dengan yang berikutnya, tetapi malah bersarang? Apakah perilaku ini didokumentasikan dengan baik dan jika ya, di mana saya dapat menemukan dokumen yang sesuai?



Sekali lagi, sorotan sintaks pada UL SE salah dan menyesatkan, meskipun JS cantiklah yang harus disalahkan.
Weijun Zhou

Jawaban:


14

Setiap konstruk bersarang yang dapat diinterpolasi di dalam string dapat memiliki string lebih lanjut di dalamnya: mereka diurai seperti skrip baru, hingga penanda penutup, dan bahkan dapat bersarang dalam beberapa level. Semua bar salah satunya dimulai dengan a $. Semuanya didokumentasikan dalam kombinasi dari spesifikasi bahasa perintah manual Bash dan POSIX shell.

Ada beberapa kasus konstruksi ini:

  • Perintahkan substitusi dengan$( ... ) , seperti yang Anda temukan. POSIX menentukan perilaku ini :

    Dengan $(command)formulir, semua karakter yang mengikuti tanda kurung buka hingga tanda kurung penutup yang cocok merupakan perintah. Setiap skrip shell yang valid dapat digunakan untuk perintah ...

    Kutipan adalah bagian dari skrip shell yang valid, jadi mereka diizinkan dengan makna normalnya.

  • Substitusi perintah `juga digunakan.
  • Elemen "kata" dari contoh substitusi parameter lanjutan seperti${parameter:-word} . The definisi "kata" adalah :

    Urutan karakter yang diperlakukan sebagai satu unit oleh shell

    - yang mencakup teks kutipan dan bahkan kutipan campuran a"b"c'd'e- meskipun perilaku sebenarnya dari ekspansi sedikit lebih liberal dari itu, dan misalnya ${x:-hello world}berfungsi juga.

  • Ekspansi aritmatika dengan $(( ... )), meskipun sebagian besar tidak berguna di sana (tetapi Anda juga dapat membuat substitusi perintah atau ekspansi variabel, dan kemudian memiliki tanda kutip yang berguna di dalamnya). POSIX menyatakan bahwa :

    Ekspresi harus diperlakukan seolah-olah dalam tanda kutip ganda, kecuali bahwa tanda kutip ganda di dalam ekspresi tidak diperlakukan secara khusus. Shell harus memperluas semua token dalam ekspresi untuk ekspansi parameter, penggantian perintah, dan penghapusan kutipan.

    jadi perilaku ini secara eksplisit diperlukan. Itu berarti echo "abc $((4 "*" 5))"tidak berhitung, bukan globbing.

    Perhatikan bahwa $[ ... ]ekspansi aritmatika gaya lama tidak diperlakukan dengan cara yang sama: kutipan akan menjadi kesalahan jika muncul, terlepas dari apakah ekspansi tersebut dikutip atau tidak. Formulir ini tidak didokumentasikan sama sekali, dan tidak dimaksudkan untuk digunakan.

  • Terjemahan khusus lokal$"..." , yang sebenarnya menggunakan "elemen inti. $"diperlakukan sebagai satu unit.

Ada satu kasus bersarang lebih lanjut yang mungkin tidak Anda harapkan, tidak melibatkan tanda kutip, yang dengan ekspansi penjepit : {a,b{c,d},e}memperluas ke "a bc bd e". ${x:-a{b,c}d}tidak tidak sarang, namun; itu diperlakukan sebagai substitusi parameter yang memberi " a{b,c", diikuti oleh " d}". Itu juga didokumentasikan :

Saat tanda kurung digunakan, tanda kurung penutup yang cocok adalah tanda '}' pertama yang tidak diloloskan oleh garis miring terbalik atau dalam string yang dikutip, dan bukan dalam ekspansi aritmatika tersemat, substitusi perintah, atau ekspansi parameter.


Sebagai aturan umum, semua konstruksi yang dibatasi mengurai tubuh mereka secara independen dari konteks sekitarnya (dan pengecualian diperlakukan sebagai bug ). Intinya, saat melihat $(kode substitusi perintah hanya meminta parser untuk mengkonsumsi apa yang bisa dari tubuh seolah-olah itu adalah program baru, dan kemudian memeriksa bahwa penanda penghentian yang diharapkan (yang tidak terhapus )atau)) atau }) muncul setelah sub-parser berjalan dari hal-hal yang dapat dikonsumsi.

Jika Anda berpikir tentang fungsi a parser keturunan-rekursif , itu hanya rekursi sederhana untuk kasus dasar. Ini sebenarnya lebih mudah dilakukan daripada sebaliknya, begitu Anda mendapatkan interpolasi string sama sekali. Terlepas dari teknik parsing yang mendasarinya, cangkang yang mendukung konstruksi ini memberikan hasil yang sama.

Anda dapat membuat sarang mengutip sebanyak yang Anda suka melalui konstruksi ini dan itu akan berfungsi seperti yang diharapkan. Tidak ada yang akan bingung dengan melihat kutipan di tengah; sebaliknya, itu akan menjadi awal dari string yang dikutip baru dalam konteks interior.


Terima kasih. Dalam "blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")", mengapa kutipan ganda kedua bukan akhir dari kutipan ganda pertama (seperti yang ditunjukkan oleh penyorotan sintaks pada balasan Anda), tetapi awal dari sebuah string di dalam $(...)? Apakah karena pengurai bash adalah top-down, bukan bottom-up?
StackExchange for All

2
Ada banyak variasi dalam penanganan "${var-"foo"}"( echo "${-+"*"}"sama seperti echo *dalam shell Bourne atau Korn misalnya) dan perilaku akan dibuat jelas tidak ditentukan dalam versi standar berikutnya . Lihat juga diskusi di mail-archive.com/austin-group-l@opengroup.org/msg00167.html
Stéphane Chazelas

3

Mungkin melihat dua contoh dengan printf(bukannya echo) akan membantu:

$ printf '<%s> ' "(echo " * ")"; echo
<(echo > <test.txt> <ppcg.sh> <file1> <file2> <file3> <)>

Mencetak (echo (kata pertama, termasuk spasi tambahan), beberapa file, dan kata penutup ).
Tanda kurung hanyalah bagian dari string yang dikutip (echo .
Tanda bintang (sekarang tidak dikutip, karena dua tanda kutip ganda dipasangkan) diperluas sebagai gumpalan ke daftar file yang cocok.
Dan kemudian, tanda kurung tutup.

Namun, perintah kedua Anda berfungsi sebagai berikut:

$ printf '<%s> ' "$(echo " * ")" ; echo
< * >

The $dimulai substitusi perintah. Itu dimulai kutipan baru.
Tanda bintang dikutip " * "dan itulah yang dihasilkan oleh perintah (di sini adalah perintah dan bukan string yang dikutip) echo. Akhirnya, printfformat ulang *dan cetak sebagai < * >.

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.