To pytanie dostaje wiele duplikatów, z których wiele nie dotyczy zmiennych, więc zwróciłem się do „wartości” zamiast do „zmiennej”. Mam nadzieję, że dzięki temu więcej osób znajdzie ten temat.
tripleee
1
@codeforester Co słychać w przypadku przywróconej edycji?
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 $URLpotrzebujesz, 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.
Zauważ, że „spacje” naprawdę oznaczają „dowolne białe znaki”.
William Pursell,
4
@Cristian: Jeśli nie jesteś pewien, co może być w zmiennej, bezpieczniej jest ją zacytować. Zwykle stosuję tę samą zasadę, co paxdiablo, i po prostu mam zwyczaj cytowania wszystkiego (chyba że istnieje konkretny powód, aby tego nie robić).
Gordon Davisson,
11
Jeśli nie znasz wartości IFS, podaj ją bez względu na wszystko. Jeśli IFS=0, to echo $?może być bardzo zaskakujące.
Charles Duffy,
3
Cytuj na podstawie kontekstu, a nie na podstawie oczekiwanych wartości, w przeciwnym razie Twoje błędy będą gorsze. Na przykład, możesz mieć pewność, że żaden z Twoich ścieżek mają spacje, więc uważasz, że można napisać cp $source1 $source2 $dest, ale jeśli za jakiś nieoczekiwany powód destnie zostanie ustawiona, trzeci argument po prostu znika i będzie cicho skopiować source1ponad source2zamiast co daje odpowiedni błąd dla pustego miejsca docelowego (tak jak w przypadku cytowania każdego argumentu).
Derek Veit
3
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).
Ed Morton,
92
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ć.
$ 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">doneFound file: file1.txt
Found file: file_other.txt
(W tym ostatnim przykładzie pętla jest całkowicie zbędna; printfszczegó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”.
Jest to wariant (części) odpowiedzi, którą zamieściłem na powiązane pytanie . Wklejam to tutaj, ponieważ jest to zwięzłe i wystarczająco zdefiniowane, aby stać się kanonicznym pytaniem dla tego konkretnego problemu.
tripleee
4
Zwrócę uwagę, że jest to pozycja nr 0 i powracający motyw na mywiki.wooledge.org/BashPitfalls zbiór typowych błędów Bash. Wiele, wiele pojedynczych pozycji na tej liście dotyczy w zasadzie tego problemu.
tripleee
27
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 .
Zgodnie z tymi wytycznymi można uzyskać listę plików w katalogu głównym, pisząc "ls" "/" Wyrażenie „wszystkie konteksty łańcuchowe” wymaga dokładniejszej kwalifikacji.
William Pursell
5
W [[ ]]cudzysłowiu ma znaczenie po prawej stronie =/ ==i =~: robi różnicę między interpretowaniem łańcucha jako wzorca / wyrażenia regularnego lub dosłownie.
Benjamin W.
6
Dobry przegląd, ale komentarze @ BenjaminW. Są warte integracji, a ciągi cytowane w ANSI C ( $'...') powinny zdecydowanie mieć własną sekcję.
mklement0
3
@ mklement0, rzeczywiście są one równoważne. Te wytyczne wskazują, że zawsze powinieneś pisać "ls" "/"zamiast bardziej powszechnych ls /, i uważam to za główną wadę w wytycznych.
William Pursell,
4
Dla żadnych cytatów można dodać zadanie lub zmienną case:)
PesaThe
4
Zazwyczaj używam cytowanych jak "$var"dla bezpieczeństwa, chyba że jestem pewien, że $varnie zawiera miejsca.
Używam $varjako prostego sposobu łączenia linii:
lines="`cat multi-lines-text-file.txt`"
echo "$lines"## multiple lines
echo $lines ## all spaces (including newlines) are zapped
Ostatni komentarz jest nieco mylący; nowe linie są skutecznie zastępowane spacjami, a nie tylko usuwane.
tripleee
-1
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)
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.
źródło
IFS=0
, toecho $?
może być bardzo zaskakujące.cp $source1 $source2 $dest
, ale jeśli za jakiś nieoczekiwany powóddest
nie zostanie ustawiona, trzeci argument po prostu znika i będzie cicho skopiowaćsource1
ponadsource2
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.
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.)
Żadne cudzysłowy nie są odpowiednie, gdy specjalnie potrzebujesz powłoki do dzielenia tokenów i / lub rozwijania symboli wieloznacznych.
Podział tokenów;
Dla kontrastu:
(Pętla działa tylko raz, nad pojedynczym cytowanym ciągiem.)
(Pętla działa tylko raz, nad dosłownym ciągiem pojedynczym.)
Rozszerzenie symboli wieloznacznych:
Dla kontrastu:
(Nie ma pliku o nazwie dosłownie
file*.txt
.)(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:(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.
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
można wkleić razem jeden po drugim, tworząc pojedynczy długi ciąg po tokenizacji i usunięciu cytatu.
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.(W tym ostatnim przykładzie pętla jest całkowicie zbędna;
printf
szczególnie dobrze działa z wieloma argumentamistat
. 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”.
źródło
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ż:
źródło
"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 powszechnychls /
, 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:źródło
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)
źródło