Czytałem, że potrzebujesz podwójnych cudzysłowów do rozwijania zmiennych, np
if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi
będzie działać zgodnie z oczekiwaniami, podczas gdy
if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi
zawsze powie, $test ok
nawet jeśli $test
jest zerowy.
ale dlaczego nie potrzebujemy cytatów echo $test
?
echo
, dodatkowe spacje i znaki nowej linii zostaną usunięte.Odpowiedzi:
Zawsze potrzebujesz cudzysłowów wokół zmiennych we wszystkich kontekstach listy , czyli wszędzie tam, gdzie zmienna może być rozwinięta do wielu wartości, chyba że chcesz, aby 3 skutki uboczne pozostawienia zmiennej nie były cytowane.
konteksty list zawierają argumenty do prostych poleceń, takich jak
[
lubecho
,for i in <here>
przypisania do tablic ... Istnieją inne konteksty, w których należy również cytować zmienne. Najlepiej jest zawsze cytować zmienne, chyba że masz bardzo dobry powód, aby tego nie robić.Pomyśl o braku cudzysłowów (w kontekście list) jako o operatorze split + glob .
Jakby
echo $test
byłoecho glob(split("$test"))
.Zachowanie powłoki jest mylące dla większości ludzi, ponieważ w większości innych języków umieszczasz cudzysłowy wokół ustalonych ciągów znaków, takich jak
puts("foo")
, a nie wokół zmiennych (jakputs(var)
), podczas gdy w powłoce jest odwrotnie: wszystko jest ciągiem znaków w powłoce, więc umieszczanie cudzysłowów wokół wszystkiego byłoby kłopotliwe, tyecho test
nie musisz"echo" "test"
. W powłoce cudzysłowy są używane do czegoś innego: zapobiegają specjalnemu znaczeniu niektórych znaków i / lub wpływają na zachowanie niektórych rozszerzeń.W
[ -n $test ]
lubecho $test
powłoka zostanie podzielona$test
(domyślnie na puste), a następnie wygeneruje nazwę pliku (rozwiń wszystkie*
wzorce „?” ... do listy pasujących plików), a następnie przekaże tę listę argumentów do poleceń[
lubecho
.Ponownie pomyśl o tym jak
"[" "-n" glob(split("$test")) "]"
. Jeśli$test
jest pusty lub zawiera tylko spacje (spc, tab, nl), to operator split + glob zwróci pustą listę, więc[ -n $test ]
będzie"[" "-n" "]"
, co jest testem sprawdzającym, czy „-n” jest pustym łańcuchem, czy nie. Ale wyobraź sobie, co by się stało, gdyby$test
„*” lub „= foo” ...W
[ -n "$test" ]
,[
jest przekazywana cztery argumenty"["
,"-n"
,""
oraz"]"
(bez cudzysłowów), który jest to, co chcemy.Niezależnie od tego, czy to robi różnicę,
echo
czy[
nie, po prostuecho
generuje to samo, niezależnie od tego, czy przekazano pusty argument, czy nie.Zobacz także tę odpowiedź na podobne pytanie, aby uzyskać więcej informacji na temat
[
polecenia i[[...]]
konstrukcji.źródło
Odpowiedź @ h3rrmiller jest dobra do wyjaśnienia, dlaczego potrzebujesz cytatów dla
if
(a raczej,[
/test
), ale tak naprawdę uznałbym, że twoje pytanie jest nieprawidłowe.Wypróbuj następujące polecenia, a zobaczysz, co mam na myśli.
Bez cudzysłowów podstawienie zmiennej powoduje rozwinięcie drugiego polecenia do:
a wiele spacji jest zwiniętych do jednego:
Dzięki cudzysłowom spacje są zachowane.
Dzieje się tak, ponieważ gdy cytujesz parametr (niezależnie od tego, czy parametr ten jest przekazywany
echo
,test
czy jakaś inna komenda), wartość tego parametru jest wysyłana jako jedna wartość do komendy. Jeśli go nie zacytujesz, powłoka wykonuje swoją normalną magię, szukając białych znaków, aby określić, gdzie zaczyna się i kończy każdy parametr.Można to również zilustrować następującym (bardzo, bardzo prostym) programem C. Wypróbuj poniższe w wierszu poleceń (możesz to zrobić w pustym katalogu, aby nie ryzykować zastąpienia czegoś).
i wtedy...
Po uruchomieniu
paramtest
,$?
odbędzie się szereg parametrów zostało przekazanych (i że numer zostanie podany).źródło
Chodzi o to, jak powłoka interpretuje wiersz przed uruchomieniem programu.
Jeśli wiersz jest czytany
echo I am $USER
, powłoka rozwija goecho I am blrfl
iecho
nie ma pojęcia, czy początek tekstu jest dosłowny czy zmienny. Podobnie, jeśli linia będzie czytaćecho I am $UNDEFINED
, powłoka rozwinie się$UNDEFINED
w nic i argumenty echa będąI am
, i to jest koniec. Ponieważecho
działa dobrze bez argumentów,echo $UNDEFINED
jest całkowicie poprawny.Twój problem z
if
tak naprawdę nie jestif
, ponieważif
po prostu uruchamia dowolny program i argumenty za nim i wykonujethen
część, jeśli program się kończy0
(lubelse
część, jeśli istnieje, a program kończy się inaczej0
):Kiedy używasz
if [ ... ]
do porównania, nie używasz prymitywów wbudowanych w powłokę. W rzeczywistości instruujesz powłokę, aby uruchamiała program o nazwie,[
który jest bardzo niewielkim nadzbioremtest(1)
tego wymaganym ostatnim argumentem]
. Oba programy kończą działanie,0
jeśli warunek testu spełni się, a1
jeśli nie.Niektóre testy przerywają się, gdy zmienna jest niezdefiniowana, ponieważ
test
nie widać, że używasz zmiennej. Ergo,[ $UNDEFINED -eq 2 ]
psuje się, ponieważ zanim skończona jest z nim powłoka,test
widoczne są tylko argumenty-eq 2 ]
, co nie jest poprawnym testem. Jeśli zrobiłeś to z czymś zdefiniowanym, np. Działałoby to[ $DEFINED -ne 0 ]
, ponieważ powłoka rozszerzyłaby go do ważnego testu (np0 -ne 0
.).Jest różnica semantyczna między
foo $UNDEFINED bar
, która rozwija się do dwóch argumentów (foo
ibar
), ponieważ$UNDEFINED
zasłużyła na swoją nazwę. Porównaj to zfoo "$UNDEFINED" bar
, które rozwija się do trzech argumentów (foo
pusty ciąg i `bar). Cudzysłowy zmuszają powłokę do interpretowania ich jako argumentu, czy coś jest między nimi, czy nie.źródło
Bez cudzysłowów
$test
może rozwinąć się w więcej niż jedno słowo, dlatego trzeba je zacytować, aby nie złamać składni, ponieważ każdy przełącznik w[
poleceniu oczekuje jednego argumentu, co robią cudzysłowy (powoduje, że cokolwiek$test
rozwija się w jeden argument)Powodem, dla którego nie potrzebujesz cudzysłowów, aby rozwinąć zmienną,
echo
jest to, że nie oczekuje ona jednego argumentu. Po prostu wydrukuje to, co mu powiesz. Więc nawet jeśli$test
rozwinie się do 100 słów, echo nadal go wydrukuje.Spójrz na Bash Pitfalls
źródło
echo
?echo
. Co sprawia, że myślisz inaczej?echo $test
i to działa (wyświetla wartość $ test)Puste parametry są usuwane, jeśli nie są cytowane:
Wywołane polecenie nie widzi, że w wierszu poleceń powłoki był pusty parametr. Wygląda na to, że [jest zdefiniowane tak, aby zwracało 0 dla -n bez następowania. Dlaczego zawsze.
Cytowanie ma również wpływ na echo w kilku przypadkach:
źródło
echo
, to skorupa. Zobaczysz to samo zachowanie zls
. Poświęćtouch '*'
trochę czasu na przygodę.:)