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:
x="("
[ "$x" = "(" ] && echo yes || echo no
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,
[ '1' = '1' ] && echo yes || echo no
wyprowadza yes
i jest oczywiście prawidłowym wyrażeniem; ale jeśli połączymy te dwa,
[ "$x" = "(" -a '1' = '1' ] && echo yes || echo no
Bash odrzuca wyrażenie wtedy i tylko wtedy, gdy x
jest (
lub !
.
Gdybyśmy mieli uruchomić powyższe za pomocą rzeczywistego [
programu, tj
'/usr/bin/[' "$x" = "(" -a '1' = '1' ] && echo yes || echo no
błą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” znak x
przed ciągami (obie wartości są porównywane), aby zapewnić rozpoznanie wyrażenia jako porównania ciągów:
[ "x$x" = "x(" -a "x$y" = "x1" ]
[[ "$x" = '1' && "$y" = '1' ]]