Czy ktoś mógłby mi powiedzieć, czy powinienem zawijać cudzysłowy wokół zmiennych w skrypcie powłoki?
Na przykład, czy następująca poprawność:
xdg-open $URL
[ $? -eq 2 ]
lub
xdg-open "$URL"
[ "$?" -eq "2" ]
A jeśli tak, to dlaczego?
Czy ktoś mógłby mi powiedzieć, czy powinienem zawijać cudzysłowy wokół zmiennych w skrypcie powłoki?
Na przykład, czy następująca poprawność:
xdg-open $URL
[ $? -eq 2 ]
lub
xdg-open "$URL"
[ "$?" -eq "2" ]
A jeśli tak, to dlaczego?
Odpowiedzi:
Ogólna zasada: podaj ją, jeśli może być pusta lub zawierać spacje (lub dowolne białe znaki) lub znaki specjalne (symbole wieloznaczne). Brak cytowania ciągów ze spacjami często prowadzi do tego, że powłoka dzieli jeden argument na wiele.
$?
nie potrzebuje cudzysłowów, ponieważ jest to wartość liczbowa. To, czy $URL
potrzebujesz, zależy od tego, na co pozwalasz, i czy nadal chcesz argumentu, jeśli jest pusty.
Zazwyczaj cytuję struny po prostu z przyzwyczajenia, ponieważ w ten sposób jest to bezpieczniejsze.
IFS=0
, to echo $?
może być bardzo zaskakujące.
cp $source1 $source2 $dest
, ale jeśli za jakiś nieoczekiwany powód dest
nie zostanie ustawiona, trzeci argument po prostu znika i będzie cicho skopiować source1
ponad source2
zamiast co daje odpowiedni błąd dla pustego miejsca docelowego (tak jak w przypadku cytowania każdego argumentu).
quote it if...
ma proces myślowy wstecz - cytaty nie są czymś, co dodajesz, kiedy jest to konieczne, lecz są usuwane, kiedy jest to konieczne. Zawsze zawijaj ciągi i skrypty w pojedyncze cudzysłowy, chyba że musisz użyć podwójnych cudzysłowów (np. Aby pozwolić zmiennej rozwinąć się) lub nie musisz używać cudzysłowów (np. W celu globowania i interpretacji nazw plików).
Krótko mówiąc, cytuj wszystko, gdzie powłoka nie wymaga dzielenia tokenów i rozwijania symboli wieloznacznych.
Pojedyncze cudzysłowy chronią tekst między nimi dosłownie. Jest to właściwe narzędzie, gdy trzeba upewnić się, że skorupa w ogóle nie dotyka sznurka. Zazwyczaj jest to mechanizm cytowania z wyboru, gdy nie jest wymagana zmienna interpolacja.
$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change
$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.
Podwójne cudzysłowy są odpowiednie, gdy wymagana jest zmienna interpolacja. Przy odpowiednich dostosowaniach jest to również dobre obejście, gdy potrzebujesz pojedynczych cudzysłowów w ciągu. (Nie ma prostego sposobu na uniknięcie pojedynczego cudzysłowu między pojedynczymi cudzysłowami, ponieważ w pojedynczych cudzysłowach nie ma mechanizmu ucieczki - gdyby tak było, nie cytowałyby one całkowicie dosłownie.)
$ echo "There is no place like '$HOME'"
There is no place like '/home/me'
Żadne cudzysłowy nie są odpowiednie, gdy specjalnie potrzebujesz powłoki do dzielenia tokenów i / lub rozwijania symboli wieloznacznych.
Podział tokenów;
$ words="foo bar baz"
$ for word in $words; do
> echo "$word"
> done
foo
bar
baz
Dla kontrastu:
$ for word in "$words"; do echo "$word"; done
foo bar baz
(Pętla działa tylko raz, nad pojedynczym cytowanym ciągiem.)
$ for word in '$words'; do echo "$word"; done
$words
(Pętla działa tylko raz, nad dosłownym ciągiem pojedynczym.)
Rozszerzenie symboli wieloznacznych:
$ pattern='file*.txt'
$ ls $pattern
file1.txt file_other.txt
Dla kontrastu:
$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory
(Nie ma pliku o nazwie dosłownie file*.txt
.)
$ ls '$pattern'
ls: cannot access $pattern: No such file or directory
(Nie ma też żadnego pliku o nazwie $pattern
!)
Mówiąc bardziej konkretnie, wszystko, co zawiera nazwę pliku, powinno być zwykle cytowane (ponieważ nazwy plików mogą zawierać białe znaki i inne metaznaki powłoki). Wszystko, co zawiera adres URL, powinno być zwykle cytowane (ponieważ wiele adresów URL zawiera metaznaki powłoki, takie jak ?
i &
). Wszystko, co zawiera wyrażenie regularne, powinno być zwykle cytowane (ditto ditto). Wszystko, co zawiera znaczące spacje inne niż pojedyncze spacje między znakami niebiałymi, musi zostać zacytowane (ponieważ w przeciwnym razie powłoka będzie munge spacja w, skutecznie, pojedyncze spacje i przyciąć wszelkie wiodące lub końcowe białe spacje).
Gdy wiesz, że zmienna może zawierać tylko wartość, która nie zawiera metaznaków powłoki, cytowanie jest opcjonalne. Zatem niecytowany $?
jest w zasadzie w porządku, ponieważ ta zmienna może zawsze zawierać tylko jedną liczbę. Jest jednak "$?"
również poprawny i zalecany dla ogólnej spójności i poprawności (chociaż jest to moja osobista rekomendacja, a nie powszechnie uznana polityka).
Wartości, które nie są zmiennymi, zasadniczo podlegają tym samym regułom, ale można również uciec od wszelkich metaznaków zamiast je cytować. W typowym przykładzie adres URL z &
nim będzie analizowany przez powłokę jako polecenie w tle, chyba że metaznak zostanie zmieniony lub cytowany:
$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found
(Oczywiście dzieje się tak również wtedy, gdy adres URL znajduje się w zmiennej niecytowanej.) W przypadku ciągu statycznego najbardziej sensowne są pojedyncze cudzysłowy, chociaż tutaj działa jakakolwiek forma cytowania lub zmiany znaczenia.
wget 'http://example.com/q&uack' # Single quotes preferred for a static string
wget "http://example.com/q&uack" # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack # Backslash escape
wget http://example.com/q'&'uack # Only the metacharacter really needs quoting
Ostatni przykład sugeruje także inną przydatną koncepcję, którą lubię nazywać „cytowaniem huśtawkowym”. Jeśli chcesz mieszać pojedyncze i podwójne cudzysłowy, możesz użyć ich obok siebie. Na przykład następujące ciągi cytowane
'$HOME '
"isn't"
' where `<3'
"' is."
można wkleić razem jeden po drugim, tworząc pojedynczy długi ciąg po tokenizacji i usunięciu cytatu.
$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.
Nie jest to strasznie czytelne, ale jest to powszechna technika, dlatego warto ją znać.
Nawiasem mówiąc, skrypty zwykle nie powinny być używane ls
do niczego. Aby rozwinąć symbol wieloznaczny, po prostu ... użyj go.
$ printf '%s\n' $pattern # not ``ls -1 $pattern''
file1.txt
file_other.txt
$ for file in $pattern; do # definitely, definitely not ``for file in $(ls $pattern)''
> printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt
(W tym ostatnim przykładzie pętla jest całkowicie zbędna; printf
szczególnie dobrze działa z wieloma argumentami stat
. Ale zapętlanie przez dopasowanie symboli wieloznacznych jest częstym problemem i często jest wykonywane niepoprawnie).
Zmienna zawierająca listę tokenów do zapętlenia lub symbol zastępczy do rozwinięcia jest rzadziej widoczna, dlatego czasami skracamy „cytować wszystko, chyba że wiesz dokładnie, co robisz”.
Oto ogólnie trzypunktowy wzór na cytaty:
Podwójne cytaty
W kontekstach, w których chcemy ukryć dzielenie słów i globowanie. Również w kontekstach, w których chcemy, aby literał był traktowany jako ciąg, a nie wyrażenie regularne.
Pojedyncze cytaty
W literałach łańcuchowych, w których chcemy stłumić interpolację i specjalne traktowanie ukośników odwrotnych. Innymi słowy, sytuacje, w których stosowanie podwójnych cudzysłowów byłoby niewłaściwe.
Brak cytatów
W kontekstach, w których jesteśmy absolutnie pewni, że nie występują problemy z dzieleniem wyrazów lub globowaniem lub chcemy podziału i globowania słów .
Przykłady
Podwójne cytaty
"StackOverflow rocks!"
, "Steve's Apple"
)"$var"
, "${arr[@]}"
)"$(ls)"
, "`ls`"
)"/my dir/"*
)"single'quote'delimited'string"
)"${filename##*/}"
)Pojedyncze cytaty
'Really costs $$!'
, 'just a backslash followed by a t: \t'
)'The "crux"'
)$'\n\t'
)$'{"table": "users", "where": "first_name"=\'Steve\'}'
)Brak cytatów
$$
, $?
, $#
itd.)((count++))
, "${arr[idx]}"
,"${string:start:length}"
[[ ]]
wyrażenie wewnętrzne wolne od dzielenia słów i globowania (jest to kwestia stylu, a opinie mogą się znacznie różnić)for word in $words
)for txtfile in *.txt; do ...
)~
być interpretowani jako $HOME
( ~/"some dir"
ale nie "~/some dir"
)Zobacz też:
"ls" "/"
Wyrażenie „wszystkie konteksty łańcuchowe” wymaga dokładniejszej kwalifikacji.
[[ ]]
cudzysłowiu ma znaczenie po prawej stronie =
/ ==
i =~
: robi różnicę między interpretowaniem łańcucha jako wzorca / wyrażenia regularnego lub dosłownie.
$'...'
) powinny zdecydowanie mieć własną sekcję.
"ls" "/"
zamiast bardziej powszechnych ls /
, i uważam to za główną wadę w wytycznych.
case
:)
Zazwyczaj używam cytowanych jak "$var"
dla bezpieczeństwa, chyba że jestem pewien, że $var
nie zawiera miejsca.
Używam $var
jako prostego sposobu łączenia linii:
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped
Aby użyć zmiennych w skrypcie powłoki, użyj „” cytowanych zmiennych, ponieważ cytowany oznacza, że zmienna może zawierać spacje lub znaki specjalne, które nie wpłyną na wykonanie skryptu powłoki. W przeciwnym razie, jeśli masz pewność, że w nazwie zmiennej nie ma żadnych spacji ani znaków specjalnych, możesz ich użyć bez „”.
Przykład:
echo „$ url name” - (można używać przez cały czas)
echo „$ url name” - (Nie można używać w takich sytuacjach, więc należy zachować ostrożność przed użyciem)