Regola generale:citalo se può essere vuoto o contenere spazi (o qualsiasi spazio bianco in realtà) o caratteri speciali (caratteri jolly). Non citare stringhe con spazi spesso porta la shell a dividere un singolo argomento in molti.
$?
non ha bisogno di virgolette poiché è un valore numerico. Se $URL
ha bisogno dipende da cosa permetti lì e se vuoi ancora un argomento se è vuoto.
Tendo a citare sempre le stringhe solo per abitudine poiché è più sicuro in questo modo.
In breve, cita tutto ciò in cui non è necessario che la shell esegua la suddivisione dei token e l'espansione dei caratteri jolly.
Le virgolette singole proteggono il testo tra di loro alla lettera. È lo strumento adatto quando è necessario assicurarsi che il fusto non tocchi affatto la corda. In genere, è il meccanismo di quotazione preferito quando non è richiesta l'interpolazione di variabili.
$ 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.
Le virgolette doppie sono adatte quando è richiesta l'interpolazione variabile. Con adattamenti adeguati, è anche una buona soluzione quando hai bisogno di virgolette singole nella stringa. (Non c'è un modo semplice per sfuggire a una citazione singola tra virgolette singole, perché non c'è alcun meccanismo di fuga all'interno delle virgolette singole -- se ci fosse, non citerebbero completamente alla lettera.)
$ echo "There is no place like '$HOME'"
There is no place like '/home/me'
Nessuna virgoletta è adatta quando richiedi specificamente che la shell esegua la suddivisione dei token e/o l'espansione dei caratteri jolly.
Divisione dei token;
$ words="foo bar baz"
$ for word in $words; do
> echo "$word"
> done
foo
bar
baz
Al contrario:
$ for word in "$words"; do echo "$word"; done
foo bar baz
(Il ciclo viene eseguito solo una volta, sulla singola stringa tra virgolette.)
$ for word in '$words'; do echo "$word"; done
$words
(Il ciclo viene eseguito solo una volta, sopra la stringa letterale tra apici singoli.)
Espansione con caratteri jolly:
$ pattern='file*.txt'
$ ls $pattern
file1.txt file_other.txt
Al contrario:
$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory
(Non c'è nessun file chiamato letteralmente file*.txt
.)
$ ls '$pattern'
ls: cannot access $pattern: No such file or directory
(Non c'è nessun file chiamato $pattern
, neanche!)
In termini più concreti, qualsiasi cosa contenga un nome di file dovrebbe di solito essere citata (poiché i nomi di file possono contenere spazi bianchi e altri metacaratteri della shell). Qualunque cosa contenga un URL di solito dovrebbe essere citata (poiché molti URL contengono metacaratteri di shell come ?
e &
). Qualunque cosa contenga una regex di solito dovrebbe essere citata (idem idem). Qualunque cosa contenga spazi bianchi significativi diversi dai singoli spazi tra caratteri non spazi bianchi deve essere citata (perché altrimenti la shell mungerà gli spazi bianchi in, effettivamente, spazi singoli e taglierà eventuali spazi bianchi iniziali o finali).
Quando sai che una variabile può contenere solo un valore che non contiene metacaratteri di shell, le virgolette sono facoltative. Quindi, un $?
non quotato va fondamentalmente bene, perché questa variabile può contenere solo un singolo numero. Tuttavia, "$?"
è anche corretto e consigliato per coerenza e correttezza generale (sebbene questa sia la mia raccomandazione personale, non una politica ampiamente riconosciuta).
I valori che non sono variabili seguono fondamentalmente le stesse regole, sebbene tu possa anche sfuggire a qualsiasi metacarattere invece di citarli. Per un esempio comune, un URL con &
in esso verrà analizzato dalla shell come comando in background a meno che il metacarattere non sia sfuggito o citato:
$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found
(Ovviamente, questo accade anche se l'URL si trova in una variabile non quotata.) Per una stringa statica, le virgolette singole hanno più senso, anche se qui funziona qualsiasi forma di citazione o escape.
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
L'ultimo esempio suggerisce anche un altro concetto utile, che mi piace chiamare "quotazione altalenante". Se hai bisogno di mescolare virgolette singole e doppie, puoi usarle l'una accanto all'altra. Ad esempio, le seguenti stringhe tra virgolette
'$HOME '
"isn't"
' where `<3'
"' is."
possono essere incollati uno dopo l'altro, formando un'unica lunga stringa dopo la tokenizzazione e la rimozione delle virgolette.
$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.
Non è molto leggibile, ma è una tecnica comune e quindi utile da sapere.
Per inciso, gli script di solito non dovrebbero usare ls
per qualsiasi cosa. Per espandere un carattere jolly, semplicemente... usalo.
$ 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
(Il ciclo è completamente superfluo nell'ultimo esempio; printf
funziona specificamente bene con più argomenti. stat
anche. Ma il loop su una corrispondenza con caratteri jolly è un problema comune e spesso eseguito in modo errato.)
Una variabile contenente un elenco di token su cui eseguire il loop o un carattere jolly da espandere viene vista meno frequentemente, quindi a volte abbreviamo in "cita tutto a meno che tu non sappia esattamente cosa stai facendo".
Ecco una formula in tre punti per le virgolette in generale:
Doppie virgolette
In contesti in cui vogliamo sopprimere la suddivisione delle parole e il globbing. Anche in contesti in cui vogliamo che il letterale sia trattato come una stringa, non come una regex.
Virgolette singole
Nei valori letterali stringa in cui vogliamo sopprimere l'interpolazione e il trattamento speciale delle barre rovesciate. In altre parole, situazioni in cui l'uso delle virgolette sarebbe inappropriato.
Niente virgolette
In contesti in cui siamo assolutamente sicuri che non ci siano problemi di suddivisione delle parole o globbing o vogliamo la suddivisione delle parole e il globbing .
Esempi
Doppie virgolette
- stringhe letterali con spazi bianchi (
"StackOverflow rocks!"
,"Steve's Apple"
) - espansioni variabili (
"$var"
,"${arr[@]}"
) - sostituzioni di comando (
"$(ls)"
,"`ls`"
) - glob dove il percorso della directory o la parte del nome del file include spazi (
"/my dir/"*
) - per proteggere le virgolette singole (
"single'quote'delimited'string"
) - Espansione del parametro Bash (
"${filename##*/}"
)
Virgolette singole
- nomi di comando e argomenti che contengono spazi bianchi
- stringhe letterali che richiedono l'interpolazione per essere soppresse (
'Really costs $$!'
,'just a backslash followed by a t: \t'
) - per proteggere le virgolette doppie (
'The "crux"'
) - letterali regex che necessitano di interpolazione per essere soppressi
- usa le virgolette della shell per i letterali che includono caratteri speciali (
$'\n\t'
) - usa le virgolette della shell dove abbiamo bisogno di proteggere diverse virgolette singole e doppie (
$'{"table": "users", "where": "first_name"=\'Steve\'}'
)
Niente virgolette
- intorno a variabili numeriche standard (
$$
,$?
,$#
ecc.) - in contesti aritmetici come
((count++))
,"${arr[idx]}"
,"${string:start:length}"
- all'interno di
[[ ]]
espressione libera da problemi di suddivisione delle parole e globbing (questa è una questione di stile e le opinioni possono variare notevolmente) - dove vogliamo la suddivisione delle parole (
for word in $words
) - dove vogliamo il globbing (
for txtfile in *.txt; do ...
) - dove vogliamo
~
da interpretare come$HOME
(~/"some dir"
ma non"~/some dir"
)
Vedi anche:
- Differenza tra virgolette singole e doppie in Bash
- Quali sono le variabili shell speciali del simbolo del dollaro?
- Citazioni e fuga - Wiki di Bash Hackers
- Quando sono necessarie le doppie virgolette?