Kiedyś byłem pewien, że cytowanie ciągów jest zawsze dobrą praktyką, aby uniknąć parsowania powłoki.
Potem natrafiłem na to:
$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1
Próbując wyizolować problem, otrzymujesz ten sam błąd:
$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1
Rozwiązałem problem w ten sposób:
[ "$x" = '1' ] && [ "$y" = '1' ]
Nadal muszę wiedzieć, co się tutaj dzieje.
[[ "$x" = '1' && "$y" = '1' ]]
-a
i-o
jest przestarzała (co oznacza[OB]
indeks górny obok ich specyfikacji). Jeśli[ "$x" = 1 ] && [ "$y" = 1 ]
zamiast tego napisałeś , wszystko byłoby w porządku i dobrze by było w obrębie dobrze zdefiniowanego / znormalizowanego zachowania.[ "x$x" = "x1" ]
aby zapobiegać błędnej interpretacji argumentów jako operatorów.dash
Bash, to nadal jest to przydatna praktyka.Odpowiedzi:
Jest to bardzo niejasny przypadek narożny, w którym można rozważyć błąd w
[
definiowaniu wbudowanego testu ; jednak pasuje do zachowania rzeczywistego[
pliku binarnego dostępnego w wielu systemach. O ile mogę powiedzieć, że dotyka tylko niektórych przypadkach i zmienną mającą wartość pasujący do[
operatora jak(
,!
,=
,-e
, i tak dalej.Pozwól mi wyjaśnić, dlaczego i jak obejść to w powłokach Bash i POSIX.
Wyjaśnienie:
Rozważ następujące:
Nie ma problemu; powyższe nie powoduje błędu, i wyniki
yes
. W ten sposób oczekujemy, że wszystko zadziała. Możesz zmienić ciąg porównania na,'1'
jeśli chcesz, i wartośćx
, i będzie działać zgodnie z oczekiwaniami.Zauważ, że rzeczywisty
/usr/bin/[
plik binarny zachowuje się w ten sam sposób. Jeśli uruchomisz np. Nie'/usr/bin/[' '(' = '(' ']'
wystąpi błąd, ponieważ program może wykryć, że argumenty składają się z operacji porównywania jednego ciągu.Błąd występuje, gdy my i z drugim wyrażeniem. Nie ma znaczenia, jakie jest drugie wyrażenie, o ile jest poprawne. Na przykład,
wyprowadza
yes
i jest oczywiście prawidłowym wyrażeniem; ale jeśli połączymy te dwa,Bash odrzuca wyrażenie wtedy i tylko wtedy, gdy
x
jest(
lub!
.Gdybyśmy mieli uruchomić powyższe za pomocą rzeczywistego
[
programu, tjbłąd byłby zrozumiały: ponieważ powłoka dokonuje podstawień zmiennych,
/usr/bin/[
plik binarny odbiera tylko parametry(
=
(
-a
1
=
1
i zakończenie]
, co zrozumiałe, nie analizuje, czy otwarte nawiasy rozpoczynają podwyrażenie, czy nie, z udziałem operacji i . Jasne, parsowanie go jako dwóch porównań ciągów jest możliwe, ale robienie tego zachłannie w ten sposób może powodować problemy po zastosowaniu do odpowiednich wyrażeń za pomocą nawiasów podwyrażeniowych.Problem polega na tym, że
[
wbudowana powłoka zachowuje się w ten sam sposób, jakby zwiększała wartośćx
przed sprawdzeniem wyrażenia.(Te dwuznaczności i inne związane z rozszerzaniem zmiennych były głównym powodem, dla którego Bash zaimplementował i teraz zaleca używanie
[[ ... ]]
zamiast tego wyrażeń testowych).Obejście jest trywialne i często występuje w skryptach używających starszych
sh
powłok. Często dodaje się „bezpieczny” znakx
przed ciągami (obie wartości są porównywane), aby zapewnić rozpoznanie wyrażenia jako porównania ciągów:źródło
[
wbudowanego błędu błędem. Jeśli już, to nieodłączna wada projektowa.[[
jest słowem kluczowym powłoki , a nie tylko wbudowanym poleceniem, więc przed usunięciem cudzysłowu może patrzeć na rzeczy i faktycznie zastępuje zwykłe dzielenie słów. np.[[ $x == 1 ]]
nie trzeba używać"$x"
, ponieważ[[
kontekst jest inny niż normalny. W każdym razie w ten sposób[[
można uniknąć pułapek[
. POSIX wymaga[
takiego zachowania, jak bash, a bash jest w większości zgodny z POSIX, nawet bez niego--posix
, więc zamiana[
na słowo kluczowe jest nieatrakcyjna.[
.[
jego zachowanie jest obciążone historią sprzed POSIX, zamiast tego wybrałem drugie słowo.) Osobiście unikam używania[[
w moich przykładowych skryptach, ale tylko dlatego, że jest to wyjątek od rekomendacji cytowania, o której zawsze traktuję (ponieważ pomijanie cytatów jest najczęstszym powodem błędów skryptowych, jakie widzę), i nie pomyślałem o prostym akapicie wyjaśniającym, dlaczego[[
jest wyjątkiem od reguł, bez podania mojej rekomendacji cytowania posądzać.sh
skryptach, dobrze poprzedzająca standaryzację POSIX. Korzystanie nawiasach podwyrażeń i-a
i-o
operatorów logicznych w testach /[
jest bardziej efektywne, że opierając się na szeregowaniu ekspresji (przez&&
a||
); po prostu na obecnych maszynach różnica nie ma znaczenia.&&
i||
, ale powodem jest to, że ludziom łatwiej jest je konserwować (czytać, rozumieć i modyfikować, jeśli to konieczne) bez wprowadzania błędów. Nie krytykuję twojej sugestii, a jedynie jej uzasadnienie. (W przypadku bardzo podobnych powodów.LT.
,.GE.
itp operatory porównania w FORTRAN 77 dostaje dużo bardziej przyjazne dla człowieka wersje<
,>=
itd w późniejszych wersjach.)[
akatest
widzi:test
akceptuje podwyrażenia w nawiasach; uważa więc, że lewy nawias otwiera podwyrażenie i próbuje je przeanalizować; parser widzi=
pierwszą rzecz w podwyrażeniu i myśli, że jest to domyślny test długości łańcucha, więc jest szczęśliwy; po podwyrażeniu powinien nastąpić prawy nawias, a zamiast tego parser znajdzie1
zamiast)
. I narzeka.Kiedy
test
ma dokładnie trzy argumenty, a środkowy argument jest jednym z rozpoznawanych operatorów, stosuje ten operator do pierwszego i trzeciego argumentu bez szukania podwyrażeń w nawiasach.Aby uzyskać szczegółowe informacje
man bash
, wyszukajtest expr
.Wniosek: stosowany algorytm analizowania
test
jest skomplikowany. Używaj tylko prostych wyrażeń i użyć powłoki operatorów!
,&&
a||
do ich łączenia.źródło