Dlaczego interpretacja parametrów ze spacjami bez cudzysłowów działa w podwójnych nawiasach „[[”, ale nie w pojedynczych nawiasach „[”?


85

Jestem mylony z używaniem pojedynczych lub podwójnych nawiasów. Spójrz na ten kod:

dir="/home/mazimi/VirtualBox VMs"

if [[ -d ${dir} ]]; then
    echo "yep"
fi

Działa idealnie, chociaż ciąg zawiera spację. Ale kiedy zmienię go na pojedynczy nawias:

dir="/home/mazimi/VirtualBox VMs"

if [ -d ${dir} ]; then
    echo "yep"
fi

To mówi:

./script.sh: line 5: [: /home/mazimi/VirtualBox: binary operator expected

Kiedy zmienię to na:

dir="/home/mazimi/VirtualBox VMs"

if [ -d "${dir}" ]; then
    echo "yep"
fi

To działa dobrze. Czy ktoś może wyjaśnić, co się dzieje? Kiedy powinienem przypisywać podwójne cudzysłowy wokół zmiennych, "${var}"aby zapobiec problemom powodowanym przez spacje?



Odpowiedzi:


83

Pojedynczy nawias [to tak naprawdę alias testpolecenia, nie składnia.

Jedną z wad (wielu) pojedynczego nawiasu jest to, że jeśli jeden lub więcej operandów próbuje ocenić zwrócenie pustego ciągu, będzie narzekał, że spodziewał się dwóch operandów (binarnych). Oto dlaczego ludzie tak robią [ x$foo = x$blah ], xgwarancje, że operand nigdy nie przekształci się w pusty ciąg.

Z [[ ]]drugiej strony podwójny nawias ma składnię i jest znacznie bardziej wydajny [ ]. Jak się dowiedziałeś, nie ma problemu z pojedynczym operandem, a także pozwala na większą składnię podobną do C z >, <, >=, <=, !=, ==, &&, ||operatorami.

Moje zalecenie jest następujące: Jeśli twój tłumacz jest #!/bin/bash, to zawsze używaj[[ ]]

Ważne jest, aby pamiętać, że [[ ]]nie jest obsługiwany przez wszystkie powłoki POSIX, jednak wiele powłok obsługuje takie jak zshi kshopróczbash


16
Problem pustych ciągów (i wielu innych) rozwiązuje się za pomocą cudzysłowów. „X” oznacza inny rodzaj problemów: operandy mogą być traktowane jako operatory . Na przykład kiedy $foojest !lub (lub -n... Ten problem nie ma być problemem z powłokami POSIX, w których liczba argumentów (obok [i ]) nie jest większa niż cztery.
Stéphane Chazelas,

21
Wyjaśnienie [ x$foo = x$blah ]jest tak samo błędne jak [ $foo = $bar ]. [ "$foo" = "$bar" ]jest poprawny w każdej powłoce zgodnej z POSIX, [ "x$foo" = "x$bar" ]działałby w dowolnej powłoce podobnej do Bourne'a, ale xnie dotyczy przypadków, w których masz puste łańcuchy, ale gdzie $foomoże być !lub -n...
Stéphane Chazelas

1
Interesująca semantyka w tej odpowiedzi. Nie jestem pewien, co masz na myśli, mówiąc, że to * nie * składnia . Oczywiście [nie jest to dokładnie alias test. Gdyby tak było, testzaakceptowałby zamykający nawias kwadratowy. test -n foo ]. Ale testnie [wymaga i wymaga jednego. [jest identyczny testpod wszystkimi innymi względami, ale nie tak to aliasdziała. Bash opisuje [i testjako wbudowane powłoki , ale [[jako słowo kluczowe powłoki .
kojiro

1
Cóż, w bash [jest wbudowaną powłoką, ale / usr / bin / [jest także plikiem wykonywalnym. Jest to tradycyjnie link do / usr / bin / test, ale we współczesnych GNU coreutils jest osobnym plikiem binarnym. Tradycyjna wersja testu sprawdza argv [0], aby sprawdzić, czy jest wywoływana jako [, a następnie szuka dopasowania].
Evan

Mój bash nie działa z >=i<=
Student

51

[Komenda jest zwykłym poleceń. Chociaż większość powłok zapewnia go jako wbudowaną funkcję zwiększania wydajności, przestrzega ona normalnych reguł składniowych powłoki. [jest dokładnie równoważne test, z wyjątkiem tego, że [wymaga on ]jako ostatniego argumentu i testnie robi tego.

Podwójne nawiasy kwadratowe [[ … ]]mają specjalną składnię. Zostały one wprowadzone w ksh (kilka lat później [), ponieważ [ich używanie może być kłopotliwe i [[pozwala na nowe fajne dodatki, które używają znaków specjalnych powłoki. Na przykład możesz pisać

[[ $x = foo && $y = bar ]]

ponieważ całe wyrażenie warunkowe jest analizowane przez powłokę, podczas gdy [ $x = foo && $y = bar ]najpierw zostanie podzielone na dwie komendy [ $x = fooi $y = bar ]rozdzielone przez &&operatora. Podobnie podwójne nawiasy kwadratowe umożliwiają takie rzeczy, jak składnia dopasowania wzorca, np. [[ $x == a* ]]Sprawdzenie, czy wartość xzaczyna się od a; w pojedynczych nawiasach rozwinie się a*to do listy plików, których nazwy zaczynają się aod bieżącego katalogu. Podwójne nawiasy kwadratowe zostały po raz pierwszy wprowadzone w ksh i są dostępne tylko w ksh, bash i zsh.

W nawiasach pojedynczych należy używać podwójnych cudzysłowów wokół podstawień zmiennych, tak jak w większości innych miejsc, ponieważ są one tylko argumentami polecenia (którym jest to [polecenie). W podwójnych nawiasach kwadratowych nie potrzebujesz podwójnych cudzysłowów, ponieważ powłoka nie wykonuje dzielenia ani globowania słów: analizuje wyrażenie warunkowe, a nie polecenie.

Wyjątkiem jest jednak [[ $var1 = "$var2" ]]sytuacja, w której potrzebujesz cudzysłowów, jeśli chcesz wykonać porównanie ciągów bajt-bajt, w przeciwnym razie $var2byłby to wzorzec do dopasowania $var1.

Jednej rzeczy, z którą nie można zrobić, [[ … ]]jest użycie zmiennej jako operatora. Na przykład jest to całkowicie legalne (ale rzadko przydatne):

if [ -n "$reverse_sort" ]; then op=-gt; else op=-lt; fi

if [ "$x" "$op" "$y" ]; then 

W twoim przykładzie

dir="/home/mazimi/VirtualBox VMs"
if [ -d ${dir} ]; then 

komenda wewnątrz ifjest [z 4 argumentów -d, /home/mazimi/VirtualBox, VMsi ]. Powłoka analizuje, -d /home/mazimi/VirtualBoxa następnie nie wie, co z tym zrobić VMs. Będziesz musiał zapobiec dzieleniu słów, ${dir}aby uzyskać poprawnie sformułowane polecenie.

Ogólnie rzecz biorąc, zawsze używaj podwójnych cudzysłowów wokół podstawień zmiennych i poleceń, chyba że wiesz, że chcesz wykonać dzielenie słów i globowanie wyniku. Główne miejsca, w których nie można używać podwójnych cytatów, to:

  • w zadaniu: foo=$bar(ale zauważ, że potrzebujesz podwójnych cudzysłowów w export "foo=$bar"lub w przypisaniach tablicowych, takich jak array=("$a" "$b"));
  • w caseoświadczeniu case $foo in …:;
  • wewnątrz podwójnych nawiasach wyjątkiem prawej strony =lub ==operatora (chyba, że chcesz zrobić pasujące do wzorca) [[ $x = "$y" ]].

We wszystkich tych przypadkach poprawne jest stosowanie podwójnych cudzysłowów, więc równie dobrze możesz pominąć zaawansowane reguły i używać cudzysłowów przez cały czas.


1
@StephaneChazelas Mój błąd. Okazuje się, że [rzeczywiście pochodzi z Systemu III w 1981 roku , myślałem, że jest starszy.
Gilles

8

Kiedy powinienem przypisywać podwójne cudzysłowy wokół zmiennych, "${var}"aby zapobiec problemom powodowanym przez spacje?

Domniemane w tym pytaniu jest

Dlaczego nie jest wystarczająco dobry?${variable_name}

${variable_name} nie znaczy, co myślisz…

... jeśli myślisz, że to ma coś wspólnego z problemami spowodowanymi przez przestrzenie (w wartościach zmiennych). jest do tego dobry:${variable_name}

$ bar=foo
$ bard=Shakespeare
$ echo $bard
Shakespeare
$ echo ${bar}d
food

i nic więcej! 1  nie przynosi żadnego pożytku, chyba że natychmiast podążysz za nim ze znakiem, który może być częścią nazwy zmiennej: literą (-lub-), podkreśleniem () lub cyfrą (-). I nawet wtedy możesz obejść to:${variable_name}AZaz_09

$ echo "$bar"d
food

Nie próbuję zniechęcać do jego używania - echo "${bar}d"jest to prawdopodobnie najlepsze rozwiązanie tutaj - ale zniechęcam ludzi do polegania na nawiasach klamrowych zamiast cytatów lub instynktownego stosowania nawiasów klamrowych, a następnie pytania: „Czy ja też potrzebuję cytatów ? „  Powinieneś zawsze używać cytatów, chyba że masz dobry powód, aby tego nie robić, a na pewno wiesz, co robisz.
_________________
1    z wyjątkiem, oczywiście, na tym, że hodowcy formy ekspansji parametru , na przykład, i bazować na składnią. Musisz także użyć , itp., Aby odnieść się do 10, 11, itd. Parametrów pozycyjnych - cytaty ci w tym nie pomogą.${parameter:-[word]}${parameter%[word]}${parameter}${10}${11}


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.