Dize sol parantez olduğunda kabuk destek testinde hata


27

Kabukların ayrıştırılmaması için her zaman iyi bir uygulama olduğuna dikkat çekiyordum.

Sonra buna rastladım:

$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1

Sorunu izole etmeye çalışarak aynı hatayı elde etmek:

$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1

Sorunu bu şekilde çözdüm:

[ "$x" = '1' ] && [ "$y" = '1' ]

Hala burada neler olduğunu bilmem gerekiyor.


2
Geçici bir çözüm olarak, bash içinde kullanabilirsiniz[[ "$x" = '1' && "$y" = '1' ]]
terdon

3
Test için POSIX spesifikasyonu açıkça -ave -obu sebepten dolayı eskimiş olarak tanımlamaktadır ( [OB]teknik özelliklerinin yanındaki üst kısım budur ). Yazdığın ise [ "$x" = 1 ] && [ "$y" = 1 ]bunun yerine, iyi olurdum ve iyi tanımlanmış / standardize davranış bilgisi dahilindedir olurdu.
Charles Duffy

6
Bu yüzden insanlar [ "x$x" = "x1" ]argümanların operatör olarak yanlış yorumlanmasını önlemek için kullanırlardı .
Jonathan Leffler

@JonathanLeffler: Hey, cevabımı tek bir cümleye yoğunlaştırdınız, adil değil! :) Biri dashBash yerine bir POSIX kabuğu kullanıyorsa , yine de kullanışlı bir uygulamadır.
Nominal Hayvan

Soruma cevap vermek için zaman harcayan herkese teşekkür ederim, gerçekten minnettarım :) Ayrıca soruma yaptığım hayati düzenleme için teşekkür etmek istiyorum. Bu foruma girmek bazen Alcatraz'dan kaçmanın aynı heyecanını veriyor, yanlış hareket hayatınız anlamına geliyor. Her neyse, Keşke bu yorum silinmeden önce size teşekkürlerimin ulaşmasını diliyorum
Claudio

Yanıtlar:


25

Bu, testin nasıl [tanımlandığına dair bir hata olarak düşünülebilecek çok karanlık bir durumdur ; ancak, [birçok sistemde mevcut olan gerçek ikili davranışına uymuyor . Bildiğim kadarıyla söyleyebilirim, sadece belli durumlarda ve bir eşleşen bir değeri olan bir değişken etkiler [gibi operatörü (, !, =,-e , vb.

Nedenini ve Bash ve POSIX mermilerinde nasıl çalışacağını açıklamama izin verin.


Açıklama:

Aşağıdakileri göz önünde bulundur:

x="("
[ "$x" = "(" ] && echo yes || echo no

Sorun değil; Yukarıdakiler hata vermez ve çıktı verir yes. İşlerin böyle olmasını bekliyoruz. İsterseniz karşılaştırma dizesini '1've değerini değiştirebilirsiniz.x ; beklendiği gibi çalışacaktır.

Gerçek /usr/bin/[ikili aynı şekilde davranır unutmayın . Eğer kaçarsan'/usr/bin/[' '(' = '(' ']' hata yoktur, çünkü program argümanların tek bir dize karşılaştırma işleminden oluştuğunu tespit edebilir.

Böcek biz ve ikinci bir ifadeyle olduğunda ortaya çıkar . Geçerli olduğu sürece ikinci ifadenin ne olduğu önemli değildir. Örneğin,

[ '1' = '1' ] && echo yes || echo no

çıktılar yesve açıkça geçerli bir ifadedir; ama ikisini birleştirirsek,

[ "$x" = "(" -a '1' = '1' ] && echo yes || echo no

Ancak ve ancak Bash ifadesini reddeder xolduğu (veya! .

Yukarıdakileri asıl [programı kullanarak çalıştırırsak , yani

'/usr/bin/[' "$x" = "(" -a '1' = '1' ] && echo yes || echo no

hata anlaşılabilir olacaktır: kabuk değişken ikameleri yaptığı için, /usr/bin/[ikili sadece parametreleri alır ( = ( -a 1 = 1ve sonlandırır ], açık parantezlerin bir alt ifade başlatıp başlatmayacağını, orada ve işlem . Elbette, onu iki dize karşılaştırması olarak ayrıştırmak mümkündür, ancak bunu ciddiyetle yapmak, parantezli alt ifadelerle uygun ifadelere uygulandığında sorunlara neden olabilir.

Asıl sorun, [yerleşik kabuğun x, ifadeyi incelemeden önceki değerini artırıyormuş gibi davranmasıdır .

(Bu belirsizlikler ve değişken genişlemeyle ilgili diğerleri, Bash'in uygulamasının ve şimdi [[ ... ]]bunun yerine test ifadelerinin kullanılmasını tavsiye etmesinin büyük bir nedenidir .)


Çözüm geçicidir ve genellikle eski shkabukları kullanan komut dosyalarında görülür . xİfadenin bir dize karşılaştırması olarak tanınmasını sağlamak için genellikle dizelerin önüne (her iki değer karşılaştırılır) bir "güvenli" karakter eklersiniz :

[ "x$x" = "x(" -a "x$y" = "x1" ]

1
[Yerleşik bir böceğin davranışını çağırmam . Bir şey varsa, bu doğal bir tasarım hatasıdır. [[bir kabuk anahtar kelimedir , yalnızca yerleşik bir komut değildir, bu nedenle alıntı kaldırma işleminden önce olaylara bakar ve aslında normal kelime ayırmayı geçersiz kılar. Örneğin [[ $x == 1 ]], "$x"bir [[bağlam normalden farklı olduğundan kullanmaya gerek yoktur . Neyse, [[tuzaklar bu nasıl önlenebilir [. POSIX, [olduğu gibi davranmayı gerektirir ve bash, çoğunlukla olmadan bile POSIX uyumludur --posix, bu nedenle [bir anahtar kelimeye geçmek çekici değildir.
Peter Cordes

Geçici çözümünüz POSIX tarafından önerilmez. Sadece iki çağrı yapmak için [.
Joker,

@PeterCordes: Çok doğru; iyi bir nokta. ( İlk paragrafımda tanımlanmak yerine tasarım tasarlanmış olmalıydım , ama davranışları POSIX’den önceki tarihin yükü altındaydı, bunun yerine ikinci kelimeyi seçtim.) Kişisel olarak örnek komut dosyalarımda kullanmaktan kaçınıyorum. alıntı önerisi için istisna her zaman (benimle alıntı yapmaktan kaçınmak için kod hatalarının en sık rastlanan nedenidir) ve alıntı önerimi yapmadan kurallara bir istisna olduğunu açıklamak için basit bir paragraf düşünmedim. şüpheli. [[[[[
Nominal Hayvan

@Wildcard: Hayır. POSIX bu uygulamaya karşı önermez ve önemli olan da budur. Sadece bir otoritenin bu uygulamayı önermediği için bunu kötüleştirmez. Aslında, işaret ettiğim gibi, shsenaryolarda kullanılan ve POSIX standardizasyonundan önce gelen tarihsel bir uygulamadır . Parantezli alt ifadeler ve -ave -omantıksal işleçlerin testlerde kullanılması / [ifade zincirine dayanarak daha verimli ( &&ve ve yoluyla ||); Sadece mevcut makinelerde farkın önemi yoktur.
Nominal Hayvan

@Wildcard: Bununla birlikte, şahsen test ifadelerini &&ve kullanarak zincirlemeyi tercih ediyorum ||, ancak bunun nedeni, insanların hataları eklemeden bunları korumasını (okumasını, anlamasını ve değiştirmesini) kolaylaştırıyor. Bu yüzden önerinizi eleştirmiyorum, yalnızca önerinin arkasındaki mantığı. (Çok benzer nedenlerle, .LT., .GE., FORTRAN 77 vb karşılaştırma operatörleri çok daha insan dostu versiyonu var <, >=daha sonraki sürümlerinde vb.)
Nominal Hayvan

11

[aka testgörür:

 argc: 1 2 3 4  5 6 7 8
 argv: ( = 1 -a 1 = 1 ]

testparantez içindeki alt ifadeleri kabul eder; bu yüzden sol parantezin bir alt ifade açtığını ve onu ayrıştırmaya çalıştığını düşünüyor; ayrıştırıcı =alt ifadede ilk şey olarak görür ve bunun örtük bir dize uzunluğu testi olduğunu düşünür, bu yüzden mutludur; subexpression sonra sağ parantez tarafından takip edilecek ve bunun yerine ayrıştırıcı bulur gerektiğini 1yerine ). Ve şikayet ediyor.

Ne zaman testtam olarak üç argüman vardır ve orta argüman tanınan işletmecilerinden biridir, parantez içinde subexpressions için bakmadan 1. ve 3. argümanları o operatörü uygular.

Tüm detaylar için man bash, arayın test expr.

Sonuç: Kullanılan ayrıştırma algoritması testkarmaşıktır. Yalnızca basit ifadeler kullanın ve kullanımı kabuk operatörleri !, &&ve ||bunları birleştirmek.

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.