2008-10-06 20 views
41

Widzę to często w skryptach budujących projekty wykorzystujące autotools (autoconf, automake). Kiedy ktoś chce sprawdzić wartość zmiennej powłoki, często korzystać z tego idiomu:

if test "x$SHELL_VAR" = "xyes"; then 
... 

co jest zaletą tego ponad wystarczy zaznaczyć wartość takiego:

if test $SHELL_VAR = "yes"; then 
... 

I rysunek musi być powodem, dla którego widzę to tak często, ale nie mogę zrozumieć, co to jest.

+1

Zobacz także [Celem skryptu powłoki x w '" x $ Variable "'] (http://stackoverflow.com/questions/1805663/shell-script- purpose-of-x-in-xvariable/). To pytanie jest duplikatem tego. –

+1

Zobacz także [bash test dla pustego łańcucha z 'X" "'] (http://stackoverflow.com/questions/6852612/bash-test-for-empty-string-with-x). To pytanie jest także jego duplikatem lub obejmuje większość tego samego problemu. Oba duplikaty mają dobre odpowiedzi. –

Odpowiedz

67

Jeśli używasz powłoki, które wykonuje prosty podstawienie a zmienna SHELL_VAR nie istnieje (lub jest puste), to trzeba zwrócić uwagę na przypadki brzegowe. Poniższe tłumaczenia nastąpi:

if test $SHELL_VAR = yes; then  --> if test = yes; then 
if test x$SHELL_VAR = xyes; then  --> if test x = xyes; then 

Pierwszy z nich wygeneruje błąd, ponieważ argument pięść do test zaginął. Drugi nie ma tego problemu.

Twoja sprawa tłumaczy następująco:

if test "x$SHELL_VAR" = "xyes"; then --> if test "x" = "xyes"; then 

Może wydają nieco zbędny, ponieważ posiada zarówno cytaty i „x”, ale będzie również obsługiwać zmienną ze spacjami w nim, bez podania to jako dwa argumenty dla polecenia test.

Inny powód (inny niż puste zmienne) ma związek z przetwarzaniem opcji. Jeśli piszesz:

if test "$1" = "abc" ; then ... 

i $1 ma wartość -n lub -z lub innych ważnych opcji dowodzić test, składnia jest niejednoznaczna.x z przodu zapobiega przedostawaniu się wiodącej kreski jako opcji do test.

Należy pamiętać, że zależy to od powłoki. Niektóre powłoki (według mnie, csh) będą gorzko narzekać, jeśli zmienna środowiskowa nie istnieje, a nie tylko zwraca pusty ciąg).

+0

tak, teraz wydaje się to oczywiste. – jonner

+0

Czy cytowanie nie rozwiązałoby pustego przypadku var edge 'test" $ SHELL_VAR "= yes" bardziej elegancko niż 'xyes'? Jeśli chodzi o przetwarzanie opcji, czy zmiana kolejności '' abc '= "$ 1" byłaby inną opcją? –

+0

Dlaczego pierwszy argument "test" zniknąłby i nie stałby się pustym łańcuchem? Czy różne powłoki radzą sobie z tym inaczej? – phk

1

Zrobiłem to w DOS, gdy SHELL_VAR może być niezdefiniowany.

2

wierzę jej powodu

SHELLVAR=$(true) 
if test $SHELLVAR = "yes" ; then echo "yep" ; fi 

# bash: test: =: unary operator expected 

jak również

if test $UNDEFINEDED = "yes" ; then echo "yep" ; fi 
# bash: test: =: unary operator expected 

i

SHELLVAR=" hello" 
if test $SHELLVAR = "hello" ; then echo "yep" ; fi 
# yep 

Jednak powinno to zazwyczaj pracują

SHELLVAR=" hello" 
if test "$SHELLVAR" = "hello" ; then echo "yep" ; fi 
#<no output> 

ale kiedy narzeka na wyjściu gdzieś indziej, trudno jest powiedzieć, co jej narzeka Myślę, więc

SHELLVAR=" hello" 
if test "x$SHELLVAR" = "xhello" ; then echo "yep" ; fi 

działa tak samo dobrze, ale byłoby łatwiejsze do debugowania.

1

Jeśli nie zrobisz czegoś "x $ SHELL_VAR", to jeśli $ SHELL_VAR jest niezdefiniowane, pojawi się błąd o "=" nie będącym monadycznym operatorem lub czymś w tym stylu.

9

Są dwa powody, które znam tej konwencji:

http://tldp.org/LDP/abs/html/comparison-ops.html

w teście związku, nawet powołując zmienna ciąg może nie wystarczyć. [-n ​​"$ string" -o "$ a" = "$ b"] może powodować błąd w niektórych wersjach Bash, jeśli $ string jest pusty. Bezpiecznym sposobem jest dodanie dodatkowej postaci do prawdopodobnie pustych zmiennych, ["x $ string"! = X -o "x $ a" = "x $ b"] ("x" anulowane).

Po drugie, w innych skorup niż Bash, zwłaszcza starszych, warunki badania jak „-z” do testowania zmiennej pustego nie istnieje, więc gdy ten:

if [ -z "$SOME_VAR" ]; then 
    echo "this variable is not defined" 
fi 

będą działać poprawnie w BASH, jeśli dążysz do przenoszenia w różnych środowiskach UNIX, gdzie nie możesz być pewien, że domyślną powłoką będzie Bash i czy obsługuje warunek -z, bezpieczniej będzie użyć formularza, jeśli ["x $ SOME_VAR "=" x "], ponieważ zawsze będzie to miało zamierzony efekt. Zasadniczo jest to stara sztuczka skryptowa do znajdowania pustej zmiennej, i nadal jest używana dzisiaj w celu zapewnienia kompatybilności wstecznej, pomimo dostępności czystszych metod.

17

Innym powodem, o którym jeszcze nikt nie wspomniał, jest kwestia przetwarzania opcji. Jeśli napiszesz:

if [ "$1" = "abc" ]; then ... 

a 1 $ ma wartość "-n", składnia polecenia testowego jest niejednoznaczna; nie jest jasne, co testowałeś. "X" z przodu zapobiega awariom prowadzącej deski rozdzielczej.

Musisz patrzeć na naprawdę starożytne muszle, aby znaleźć takie, w którym polecenie testu nie ma wsparcia dla -n lub -z; komenda Version 7 (1978) test je zawiera. Nie jest to bez znaczenia - niektóre pliki UNIX z wersji 6 uciekły do ​​BSD, ale w dzisiejszych czasach bardzo trudno byłoby znaleźć coś, co dawniej byłoby w użyciu.

Nie stosowanie podwójnych cudzysłowów wokół wartości jest niebezpieczne, jak wskazano wiele innych osób. Rzeczywiście, jeśli jest szansa, że ​​nazwy plików mogą zawierać spacje (MacOS X i Windows zarówno zachęcają do tego w pewnym stopniu, a Unix zawsze je wspierał, ale narzędzia takie jak xargs utrudnią), wtedy powinieneś otaczać nazwy plików podwójnymi cudzysłowami co czas też ich używasz. Jeśli nie odpowiadasz za wartość (np. Podczas obsługi opcji i ustawiasz zmienną na "nie" przy uruchomieniu i "tak", gdy flaga jest w linii poleceń), nie jest bezpiecznie używać niecytowanych form zmiennych dopóki nie udowodnisz, że są bezpieczne - możesz równie dobrze robić to cały czas w wielu celach. Albo udowodnij, że twoje skrypty zawiodą, jeśli użytkownicy spróbują przetwarzać pliki z pustymi miejscami w nazwach. (A są też inne postacie, o które trzeba się martwić - na przykład backticks może być dość nieprzyjemny.)

+3

Powiedziałbym, że nie ma dwuznaczności, gdy rdzeń testowy jest prawidłowo cytowany jak w pierwszym przykładzie. W takim przypadku istnieją 3 argumenty, nawet jeśli niektóre wartości są puste, na przykład ["-n" = ""]. Tak więc implementacja 'test' nie powinna wchodzić w gałąź [-n" $ var "] (która wymaga tylko 2 argumentów). Osobiście za każdym razem, gdy miałem problemy, były albo związane z brakującymi kwotowaniami około $ var, co powodowało błąd "=: oczekiwany operator", albo opcja -z nie istnieje, albo skomplikowane użycie 'testu' w jego rdzeniu [... ] zamiast używać wielu [test1] || [test2] na poziomie powłoki (jak wspomniano @Jay). – user1556814

+1

Jeśli możesz polegać na nowoczesnych implementacjach, być może uda Ci się uciec, choć czasami trudno jest odczytać komentarze. Historycznie niektóre implementacje 'testu' robiły dziwne rzeczy, a technika była chroniona przed tymi dziwacznymi implementacjami. Możesz mieć tyle szczęścia, że ​​nie musisz się tym martwić. –

2

polecam zamiast:

if test "yes" = "$SHELL_VAR"; then 

ponieważ eliminuje brzydki x i nadal rozwiązuje problem wymieniony przez https://stackoverflow.com/a/174288/895245 że $SHELL_VAR może zacząć - i być odczytywane jako opcja.