Mengapa `|` tidak diperlakukan secara harfiah dalam pola bola?


13

Pertanyaan saya berasal dari Bagaimana cara menyimpan ekspresi reguler dalam variabel shell menghindari masalah dengan mengutip karakter yang khusus untuk shell? .

  1. Mengapa ada kesalahan:

    $ [[ $a = a|b ]]  
    bash: syntax error in conditional expression: unexpected token `|'
    bash: syntax error near `|b'

    Di [[ ... ]]dalam operan kedua =diharapkan menjadi pola globbing.

    Bukankah a|bpola globbing yang valid? Bisakah Anda menunjukkan aturan sintaks yang dilanggar?

  2. Beberapa komentar di bawah menunjukkan bahwa |diartikan sebagai pipa.

    Kemudian mengubah =untuk pola glob menjadi =~untuk pola regex membuat |pekerjaan

    $ [[ $a =~ a|b ]]

    Saya belajar dari Learning Bash p180 di posting saya sebelumnya yang |diakui sebagai pipa pada awal interpretasi, bahkan sebelum langkah interpretasi lainnya (termasuk mengurai ekspresi kondisional dalam contoh). Jadi bagaimana bisa |dikenali sebagai operator regex saat menggunakan =~, tanpa dikenali sebagai pipa yang digunakan tidak valid, seperti halnya saat menggunakan =? Itu membuat saya berpikir bahwa kesalahan sintaksis di bagian 1 tidak berarti |ditafsirkan sebagai pipa.

    Setiap baris yang dibaca shell dari input standar atau skrip disebut pipeline; ini berisi satu atau lebih perintah yang dipisahkan oleh nol atau lebih karakter pipa (|). Untuk setiap pipa yang dibacanya, shell memecahnya menjadi perintah, mengatur I / O untuk pipa, kemudian melakukan hal berikut untuk setiap perintah (Gambar 7-1):

Terima kasih.


1
Perhatikan bahwa di beberapa versi bash, parsing extglob (di mana |khusus) diaktifkan secara default di sisi kanan [[ $var = $pattern ]]. Akan menarik untuk mengisolasi shoptkonfigurasi versi dan opsi di mana perilaku ini terlihat - jika hanya itu extglobyang aktif, baik dengan konfigurasi default atau eksplisit, well, kita ada di sana.
Charles Duffy

2
BTW, jika Anda ingin mengesampingkan secara menyeluruh kasus karakter pipa yang mengganggu tahap penguraian sebelumnya (yang saya setuju tidak terjadi, tetapi tidak terlalu jelas bagi pembaca), Anda akan gunakan pattern='a|b'dan kemudian perluas tanda $patternkutip pada RHS.
Charles Duffy

@CharlesDuffy, itulah gunanya Tanya Jawab yang merupakan tindak lanjut dari pertanyaan ini.
Stéphane Chazelas

Ahh - konteksnya masuk akal; dan jawaban Anda di sini luar biasa. Terima kasih untuk keduanya.
Charles Duffy

Tim, apakah ada jawaban di bawah ini yang menjawab pertanyaan Anda? Silakan pertimbangkan untuk menerima satu jika demikian. Terima kasih!
Jeff Schaller

Jawaban:


13

Tidak ada alasan mengapa

[[ $a = a|b ]]

Harus melaporkan kesalahan alih-alih menguji apakah $ a adalah a|bstring, sementara [[ $a =~ a|b ]]tidak mengembalikan kesalahan.

Satu-satunya alasan adalah bahwa |umumnya (di luar dan di dalam [[ ... ]]) karakter khusus. Di [[ $a =posisi itu, bashmengharapkan jenis token yang merupakan KATA normal seperti argumen atau target pengalihan dalam baris perintah shell normal (tetapi seolah-olah extglobopsi telah diaktifkan sejak bash 4.1).

(oleh WORD di sini, saya merujuk pada a kata dalam tata bahasa shell hipotetis seperti yang dijelaskan oleh spesifikasi POSIX , itu adalah sesuatu yang shell akan parse sebagai salah satu token dalam baris perintah shell sederhana, bukan definisi lain dari kata-kata seperti bahasa Inggris salah satu dari urutan huruf atau urutan karakter non-spasi. foo"bar baz", $(echo x y), dua seperti WORD s).

Dalam baris perintah shell normal:

echo a|b

Apakah echo adisalurkan ke b. a|bbukan KATA , itu tiga token: aa KATA , |token dan b KATA token.

Saat digunakan di [[ $a = a|b ]] , bashmengharapkan WORD yang didapat ( a), tetapi kemudian menemukan |token yang tidak terduga yang menyebabkan kesalahan.

Menariknya, bashtidak mengeluh dalam:

[[ $a = a||b ]]

Karena sekarang a a token diikuti oleh ||token diikuti oleh b, jadi diuraikan dengan cara yang sama seperti:

[[ $a = a || b ]]

Yang sedang menguji bahwa $aadalah aatau bahwa bstring non-kosong.

Sekarang di:

[[ $a =~ a|b ]]

bashtidak dapat memiliki aturan penguraian yang sama. Memiliki aturan penguraian yang sama akan berarti bahwa di atas akan memberikan kesalahan dan bahwa seseorang perlu mengutip bahwa |untuk memastikan a|badalah tunggal KATA . Tapi, sejak bash 3.2, jika Anda melakukannya:

[[ $a =~ 'a|b' ]]

Itu tidak lagi cocok dengan a|bregexp tetapi terhadap a\|bregexp. Artinya, mengutip shell memiliki efek samping menghilangkan makna khusus dari operator regexp. Ini fitur, jadi perilakunya mirip dengan yang [[ $a = "?" ]]ada, tetapi pola wildcard (digunakan dalam[[ $a = pattern ]] ) shell WORDS (digunakan dalam gumpalan misalnya), sedangkan regexps tidak.

Jadi bashharus memperlakukan semua operator regexp yang diperluas yang biasanya karakter shell khusus seperti| , (, )berbeda ketika parsing argumen dari =~operator.

Tetap, perhatikan itu sementara

 [[ $a =~ (ab)*c ]]

sekarang bekerja,

 [[ $a =~ [)}] ]]

tidak. Anda membutuhkan:

 [[ $a =~ [\)}] ]]
 [[ $a =~ [')'}] ]]

Yang dalam versi sebelumnya bash salah cocok dengan backslash. Yang itu sudah diperbaiki, tapi

 [[ $a =~ [^]')'] ]]

Apakah tidak cocok di backslash seperti seharusnya misalnya. Karena bashgagal menyadari bahwa )ada di dalam kurung, maka lolos )ke menghasilkan [^]\)]regexp yang cocok dengan karakter apa pun tetapi ],\ dan ).

ksh93 memiliki bug jauh lebih buruk di bagian depan itu.

Dalam zsh, itu adalah kata shell normal yang diharapkan dan mengutip operator regexp tidak mempengaruhi arti dari operator regexp.

[[ $a =~ 'a|b' ]]

Cocok dengan a|bCocok regexp.

Itu berarti =~dapat juga ditambahkan ke [/ testperintah:

[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'

(Juga bekerja di yash. =~Kebutuhan dikutip dalamzsh seperti =somethingoperator shell khusus di sana).

bash 3.1 dulu berperilaku seperti zsh. Itu berubah di 3.2, mungkin untuk menyelaraskan dengan ksh93(meskipun bashshell yang pertama kali muncul dengan [[ =~ ]]), tetapi Anda masih bisa melakukan BASH_COMPAT=31atau shopt -s compat31kembali ke perilaku sebelumnya (kecuali bahwa sementara [[ $a =~ a|b ]]akan mengembalikan kesalahan dalam bash3.1, itu tidak lagi dibash -O compat31 dengan versi yang lebih baru bash).

Semoga ini menjelaskan mengapa saya mengatakan aturannya membingungkan dan mengapa menggunakan:

[[ $a =~ $var ]]

membantu termasuk dengan portabilitas ke shell lain.


zsh juga melaporkan kesalahan pada [[ $a = a|b ]].
Isaac

@isaac, ya, itulah maksud saya di sini. a|bbukan KATA shell di sini, itu a, |dan btoken. Suka echo a|btidak menghasilkan a|batau tidak memperluas a|bgumpalan, Anda perlu mengutip bahwa |itu adalah karakter shell khusus yang tidak valid dalam konteks itu. [[ $a = (a|b) ]]akan bekerja seperti echo (a|b)akan bekerja seperti (a|b)operator wildcard zsh.
Stéphane Chazelas

Kata-kata dan penjelasan tentang jawaban Anda hanya nama bash. Itu bukan seluruh kebenaran.
Isaac

11

Gumpalan standar ( "ekspansi nama file") adalah: *, ?, dan[ ... ] . |bukan operator glob yang valid dalam pengaturan standar (non-extglob).

Mencoba:

shopt -s extglob
[[ a = @(a|b) ]] && echo matched

1
Terima kasih. Tetapi mengapa tidak |diintepretasikan secara harfiah? Mengapa ada kesalahan sintaksis?
Tim

1
Itu tidak dikutip.
Jeff Schaller

3
Dalam pengaturan standar, |bukankah operator glob, jadi tidak |ditafsirkan secara literal tanpa dikutip? Jadi mengapa ada kesalahan sintaksis?
Tim

1
|adalah karakter kontrol; itu tidak pernah diperlakukan sebagai karakter literal dengan cara yang sama seperti huruf atau angka.
chepner

3
Karena dalam mode itu shell tidak mengharapkan karakter pengalihan pipa di tengah [[]] yang belum ditutup. [[ $a = abukan perintah yang valid yang outputnya dapat disalurkan ke proses lain (setidaknya itulah yang dipikirkan shell yang Anda coba lakukan).
Jason C

5

Jika Anda ingin pencocokan regex, tesnya adalah:

[[ "$a" =~ a|b ]]

@ Tim Anda harus membuka pertanyaan baru, tidak terus-menerus mengedit pertanyaan Anda saat ini.
gardenhead

@gardenhead: Pembaruan saya adalah untuk mengklarifikasi pertanyaan saya, alih-alih mengubahnya, jika Anda melewatkannya. Bagian kedua yang saya tambahkan adalah untuk menunjukkan penjelasan pipa satu komentar tentang pertanyaan asli saya (mengapa kesalahan sintaks) tidak benar.
Tim
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.