Dawna rada polegała na podwójnym cytowaniu dowolnego wyrażenia obejmującego $VARIABLE
, przynajmniej jeśli ktoś chciałby, aby był interpretowany przez powłokę jako pojedynczy element, w przeciwnym razie wszelkie spacje w treści $VARIABLE
zrzucałyby powłokę.
Rozumiem jednak, że w nowszych wersjach powłok podwójne cytowanie nie jest już zawsze potrzebne (przynajmniej w celu opisanym powyżej). Na przykład w bash
:
% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory
Z zsh
drugiej strony te same trzy polecenia są skuteczne. Dlatego na podstawie tego eksperymentu wydaje się, że bash
wewnątrz można pominąć podwójne cudzysłowy w środku [[ ... ]]
, ale nie wewnątrz [ ... ]
ani w argumentach wiersza poleceń, podczas gdy zsh
we wszystkich tych przypadkach można pominąć podwójne cudzysłowy.
Ale wnioskowanie ogólnych reguł z niepotwierdzonych przykładów takich jak powyższy jest przypadkową propozycją. Byłoby miło zobaczyć podsumowanie, kiedy konieczne jest podwójne cytowanie. Jestem zainteresowany przede wszystkim zsh
, bash
i /bin/sh
.
SH_WORD_SPLIT
opcji.Odpowiedzi:
Najpierw oddziel zsh od reszty. To nie jest kwestia starych czy współczesnych powłok: zsh zachowuje się inaczej. Projektanci zsh postanowili uczynić go niezgodnym z tradycyjnymi powłokami (Bourne, ksh, bash), ale łatwiejszym w użyciu.
Po drugie, o wiele łatwiej jest używać podwójnych cudzysłowów przez cały czas niż pamiętać, kiedy są potrzebne. Są one potrzebne przez większość czasu, więc musisz się uczyć, kiedy nie są potrzebne, a nie kiedy są potrzebne.
W skrócie, podwójne cudzysłowy są konieczne wszędzie tam, gdzie oczekiwana jest lista słów lub wzór . Są opcjonalne w kontekstach, w których analizator oczekuje surowego ciągu.
Co dzieje się bez cytatów
Zauważ, że bez podwójnych cudzysłowów zdarzają się dwie rzeczy.
${foo}
lub wynik polecenia dla podstawienia polecenia jak$(foo)
) jest dzielony na słowa wszędzie tam, gdzie zawiera białe znaki.Dokładniej, wynik rozwinięcia jest dzielony na każdy znak, który pojawia się w wartości
IFS
zmiennej (znak separatora). Jeśli sekwencja znaków separatora zawiera spacje (spację, tabulator lub znak nowej linii), spacja jest liczona jako pojedynczy znak; wiodące, końcowe lub powtarzające się separatory niebiałe prowadzą do pustych pól. Na przykład, withIFS=" :"
,:one::two : three: :four
produkuje puste pola przedone
, pomiędzyone
itwo
oraz (jeden) pomiędzythree
ifour
.\[*?
. Jeśli ten wzór pasuje do jednej lub więcej nazw plików, wzór jest zastępowany listą pasujących nazw plików.Niecytowane rozwinięcie zmiennej
$foo
jest potocznie nazywane „operatorem podziału + operatora globalnego”, w przeciwieństwie do tego,"$foo"
który po prostu przyjmuje wartość zmiennejfoo
. To samo dotyczy podstawiania poleceń:"$(foo)"
to podstawienie polecenia,$(foo)
to zastąpienie polecenia, po którym następuje split + glob.Gdzie można pominąć podwójne cudzysłowy
Oto wszystkie przypadki, o których mogę myśleć w powłoce w stylu Bourne'a, w której można wpisać zmienną lub podstawienie polecenia bez podwójnych cudzysłowów, a wartość jest interpretowana dosłownie.
Po prawej stronie zadania.
Zauważ, że potrzebujesz podwójnych cudzysłowów po
export
, ponieważ jest to zwykłe wbudowane, a nie słowo kluczowe. Jest to prawdą tylko w niektórych powłokach, takich jak dash, zsh (w emulacji sh), yash lub posh; bash i ksh traktująexport
specjalnie.W
case
oświadczeniu.Zauważ, że potrzebujesz podwójnych cudzysłowów we wzorze przypadku. Podział słów nie występuje we wzorcu wielkości liter, ale zmienna niecytowana jest interpretowana jako wzorzec, podczas gdy zmienna cytowana jest interpretowana jako ciąg literału.
W podwójnych nawiasach. Podwójne nawiasy kwadratowe są specjalną składnią powłoki.
Tyle, że potrzebujesz podwójnych cudzysłowów tam, gdzie oczekuje się wzorca lub wyrażenia regularnego: po prawej stronie
=
lub==
lub!=
lub=~
.Potrzebujesz podwójnych cudzysłowów jak zwykle w pojedynczych nawiasach,
[ … ]
ponieważ są one zwykłą składnią powłoki (jest to polecenie, które się wywołuje[
). Zobacz nawiasy pojedyncze lub podwójneW przekierowaniu w nieinteraktywnych powłokach POSIX (nie
bash
, aniksh88
).Niektóre powłoki, gdy są interaktywne, traktują wartość zmiennej jako wzór wieloznaczny. POSIX zabrania tego zachowania w nieinteraktywnych powłokach, ale kilka powłok, w tym bash (z wyjątkiem trybu POSIX) i ksh88 (w tym gdy jest to (rzekomo) POSIX
sh
niektórych komercyjnych uniksów takich jak Solaris) nadal tobash
robi ( próbuje również podzielić i przekierowanie nie powiedzie się, chyba że rozłam + nazw plików wyników dokładnie jednego słowa), dlatego lepiej zacytować cele przekierowań wsh
skrypcie w przypadku, gdy chcesz przekonwertować go dobash
skryptu niektóre dni, lub uruchomić go w systemie, w którymsh
jest niezgodny w tym punkcie lub może pochodzić z interaktywnych powłok.Wewnątrz wyrażenia arytmetycznego. W rzeczywistości musisz pominąć cudzysłowy, aby zmienna była analizowana jako wyrażenie arytmetyczne.
Jednak potrzebujesz cudzysłowów wokół rozszerzenia arytmetycznego, ponieważ są one podzielone na słowa w większości powłok, jak wymaga POSIX (!?).
W indeksie tablicy asocjacyjnej.
W niektórych rzadkich przypadkach przydatna może być niecytowana zmienna i podstawienie polecenia:
$IFS
nie została ona zmodyfikowana i chcesz podzielić ją na białe znaki.set -f
, ustawIFS
znak separatora (lub zostaw to w spokoju, aby dzielił się spacją), a następnie wykonaj rozwinięcie.Zsh
W Zsh można przeoczyć podwójne cudzysłowy przez większość czasu, z kilkoma wyjątkami.
$var
nigdy nie rozwija się do wielu słów, jednak rozwija się do pustej listy (w przeciwieństwie do listy zawierającej pojedyncze, puste słowo), jeśli wartościąvar
jest pusty ciąg. Kontrast:Podobnie
"${array[@]}"
rozwija się do wszystkich elementów tablicy, natomiast$array
rozwija się tylko do niepustych elementów.@
Flag ekspansja parametr czasami wymaga cudzysłowie całej substytucji:"${(@)foo}"
.Podstawienie polecenia podlega podziałowi pola, jeśli nie jest cytowane:
echo $(echo 'a'; echo '*')
drukujea *
(z pojedynczą spacją), podczas gdyecho "$(echo 'a'; echo '*')"
drukuje niezmodyfikowany ciąg dwuwierszowy. Użyj,"$(somecommand)"
aby uzyskać wynik polecenia w jednym słowie, bez ostatnich znaków nowej linii. Użyj,"${$(somecommand; echo _)%?}"
aby uzyskać dokładny wynik polecenia, w tym końcowe znaki nowej linii. Użyj,"${(@f)$(somecommand)}"
aby uzyskać tablicę wierszy z danych wyjściowych polecenia.źródło
echo "$(("$expr"))"
man bash
mówi: Wyrażenie jest traktowane tak, jakby znajdowało się w podwójnych cudzysłowach, ale podwójne cudzysłowy w nawiasach nie są traktowane specjalnie.echo
. Być może warto spróbować uczynić język jeszcze bardziej wyraźnym (być może „kiedy parser oczekuje surowego ciągu znaków”)