Neden `| '' tam anlamıyla bir küre şeklinde muamele görmüyor?


13

Benim sorum , düzenli ifadeyi bir kabuk değişkeninde saklamak, kabuğa özel karakterleri alıntılamaktan kaynaklanan sorunları nasıl önler? .

  1. Neden bir hata var:

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

    İçinde [[ ... ]]ikinci terimin =bir globbing deseni olması bekleniyor.

    a|bgeçerli globbing desen değil? Hangi sözdizimi kuralını ihlal ettiğini belirtebilir misiniz?

  2. Aşağıda bazı |yorumlar, boru olarak yorumlandığına dikkat çekmektedir .

    Daha sonra =glob paterni =~için regex paterni için |çalışma

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

    Öğrendiğim Öğrenme Bash içinde P180 önceki yazıma| yorumlama başka adımlar (örneklerde koşullu ifadeler ayrıştırma dahil) önce bile, yorumlama başında boru olarak kabul edilmektedir. Peki , kullanımda olduğu gibi, geçersiz kullanımda boru |olarak tanınmadan, regex operatörü olarak nasıl tanınabilir ? Bu bana bölüm 1'deki sözdizimi hatasının bir kanal olarak yorumlandığı anlamına gelmediğini düşündürüyor .=~=|

    Kabuğun standart girdiden veya bir komut dosyasından okuduğu her satıra boru hattı denir; sıfır veya daha fazla dikey çizgi karakteri (|) ile ayrılmış bir veya daha fazla komut içerir. Okuduğu her boru hattı için, kabuk onu komutlara ayırır, boru hattı için G / Ç'yi ayarlar, ardından her komut için aşağıdakileri yapar (Şekil 7-1):

Teşekkürler.


1
Bazı bash sürümlerinde, extglob ayrıştırma işleminin ( |özel olduğu yerde ) varsayılan olarak sağ tarafında açık olduğunu unutmayın [[ $var = $pattern ]]. shoptBu davranışın görüldüğü sürümleri ve seçenek yapılandırmalarını izole etmek ilginç olacaktır - eğer sadece extglobvarsayılan veya açık yapılandırma ile açıksa, oradayız.
Charles Duffy

2
BTW, bir önceki ayrıştırma aşamasına müdahale eden boru karakterinin durumunu daha kapsamlı bir şekilde dışlamak istiyorsanız (ki bunun gerçekleşmediğini kabul ediyorum, ancak okuyucu için olabildiğince açık değil), kullanın pattern='a|b've daha sonra RHS'de $patternalıntılanmamış olarak genişletin .
Charles Duffy

Noktası oldu @CharlesDuffy, yapılmaktadır Q & A bu soru bir takip etmektir.
Stéphane Chazelas

Ahh - bağlam mantıklı; ve cevabınız olağanüstü. Her iki konuda da teşekkür ederim.
Charles Duffy

Tim, aşağıdaki cevaplardan herhangi birini sorunuzu cevaplıyor musunuz? Lütfen öyleyse kabul etmeyi düşünün. Teşekkür ederim!
Jeff Schaller

Yanıtlar:


13

Bunun için iyi bir neden yok

[[ $a = a|b ]]

$ a|bA'nın dize olup olmadığını sınamak yerine bir hata bildirmesi gerekirken [[ $a =~ a|b ]]hata döndürmez.

Bunun tek nedeni |genellikle (dış ve iç [[ ... ]]) özel bir karakter olmasıdır. Bu [[ $a =konumda, normal bir kabuk komut satırındaki argümanlar veya yeniden yönlendirme hedefleri gibi bashnormal bir WORD olan bir simge türü bekler (ancak extglobseçenek bash 4.1'den beri etkinleştirilmiş gibi).

(tarafından WORD burada, ben bir atıfta kelime gibi bir varsayımsal kabuk dilbilgisi POSIX şartnamenin açıklanana kabuk, basit bir kabuk komut satırında simge biri İngilizce gibi kelimelerin değil başka tanım olarak ayrıştırmak olacağı bir şey olduğunu, bir harf dizisinden biri veya boşluk bırakmayan karakter dizisi. foo"bar baz", $(echo x y)bu tür iki WORD'dür ).

Normal bir kabuk komut satırında:

echo a|b

Is echo ayöneltilen b. a|bbir WORD değil , üç jeton: bir a WORD , bir |jeton ve bir b WORD jetonu.

İçinde kullanıldığında [[ $a = a|b ]], aldığı bashbir WORD bekler ( a), ancak |hataya neden olan beklenmedik bir belirteç bulur .

İlginçtir, bashşikayet etmiyor:

[[ $a = a||b ]]

Artık bir ajeton, ardından bir jeton ve ardından bir ||jeton olduğundan b, şu şekilde aynı şekilde ayrıştırıldı:

[[ $a = a || b ]]

Hangi yani test ediyor $aolduğunu aveya bdize olmayan boşaltın.

Şimdi:

[[ $a =~ a|b ]]

bashaynı ayrıştırma kuralına sahip olamaz. Aynı ayrıştırma kuralına sahip olmak, yukarıdakilerin bir hata vereceği ve tek bir WORD| olduğundan emin olmak için teklif vermesi gerektiği anlamına gelir . Ancak, bash 3.2'den beri, yaparsanız:a|b

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

Bu artık a|bnormal ifadeyle değil , normal ifadeyle eşleşiyor a\|b. Yani, kabuk alıntılamanın normal ifade operatörlerinin özel anlamını kaldırmanın yan etkisi vardır. Bu bir özelliktir, bu nedenle davranış [[ $a = "?" ]]bire benzer , ancak joker karakter kalıpları (içinde kullanılır [[ $a = pattern ]]) kabuk WORDS (örneğin globlarda kullanılır), regexps ise değildir.

Yani bashbaşka türlü normal gibi özel kabuk karakterler tüm genişletilmiş regexp'in operatörleri tedavi etmek vardır |, (, )bir argüman ayrıştırma farklı zaman =~operatör.

Yine de,

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

şimdi çalışıyor,

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

yapmaz. Gerekenler:

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

Hangi önceki sürümlerinde bashters eğik çizgi üzerinde yanlış eşleşir. Bu düzeltildi, ama

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

Does değil o mesela gerektiği gibi ters eğik çizgi üzerinde uyuyor. Çünkü bashfarkına başarısız )yüzden kaçar, parantez içinde olduğuna )bir sonuçlanması [^]\)]regexp herhangi karakteri ama üzerinde maçları ], \ve ).

ksh93 o cephede çok daha kötü hatalar var.

Bu zshnormal bir kabuk kelimesidir ve normal ifade operatörlerinden alıntı yapmak normal ifade operatörlerinin anlamını etkilemez.

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

a|bNormal ifade ile eşleşiyor .

Bu =~, [/ testkomutuna da eklenebileceği anlamına gelir :

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

(ayrıca çalışır yash. Burada özel bir kabuk operatörü olarak =~belirtilmesi gerekir ).zsh=something

bash 3.1 eskiden böyle davranıyordu zsh. Muhtemelen hizalamak için 3.2'de değişti ksh93( bashilk ortaya çıkan kabuk olmasına rağmen [[ =~ ]]), ancak yine de yapabilir BASH_COMPAT=31veya shopt -s compat31önceki davranışa geri dönebilirsiniz (ancak 3.1'de [[ $a =~ a|b ]]bir hata döndürecek basholsa da, artık içinde bash -O compat31daha yeni versiyonları ile bash).

Umarım neden kuralların kafa karıştırıcı olduğunu ve neden kullandığımı açıklar:

[[ $a =~ $var ]]

diğer mermilere taşınabilirlik dahil yardımcı olur.


zsh ayrıca bir hata bildiriyor [[ $a = a|b ]].
NotAnUnixNazi

@isaac, evet, burada bahsettiğim nokta bu. burada a|bbir kabuk WORD değil a, |ve bjetonu. Gibi bir echo a|bçıktı a|bvermez veya bir a|bglob genişletmez |gibi, bu bağlamda geçersiz özel bir kabuk karakteri olduğundan alıntı gerekir . bir zsh joker karakter operatörü [[ $a = (a|b) ]]gibi echo (a|b)çalışacaktı (a|b).
Stéphane Chazelas

Cevabınızdaki ifadeler ve açıklamalar sadece bash adını verir. Bütün gerçek bu değil.
NotAnUnixNazi

11

: Standart globs ( "dosya adı genişleme") vardır *, ?ve [ ... ]. |standart (extglob olmayan) ayarlarda geçerli bir glob operatörü değildir.

Deneyin:

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

1
Teşekkürler. Ama neden |tam anlamıyla interaktif hale getirilmiyor? Neden bir sözdizimi hatası var?
Tim

1
Alıntılanmadı.
Jeff Schaller

3
Standart ayarlarda, |bir glob operatörü değil, bu yüzden |alıntı yapılmadan tam anlamıyla yorumlanmıyor mu? Öyleyse neden bir sözdizimi hatası var?
Tim

1
|bir kontrol karakteridir; hiçbir zaman bir harf veya rakam gibi gerçek bir karakter olarak değerlendirilmez.
chepner

3
Çünkü bu modda, kabuk henüz kapatılmamış bir [[]] ortasında boru yönlendirme karakteri beklemiyordu. [[ $a = açıktıları başka bir işleme geçirilebilecek geçerli bir komut değildir (en azından kabuğun yapmaya çalıştığınız şey budur).
Jason C

5

Normal ifade eşleşmesini istiyorsanız test şu şekilde olur:

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

@ Zaman Mevcut sorunuzu sürekli düzenlememeksizin yeni sorular açıyor olmalısınız.
gardenhead

@gardenhead: Güncellemem sorularımı kaçırmak yerine değiştirmek yerine açıklığa kavuşturmak. Eklediğim ikinci bölüm, orijinal sorum (sözdizimi hatasının neden) doğru olmadığına dair bir yorumun boru açıklamasını göstermek .
Tim
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.