Il vecchio consiglio era quello di virgolettere qualsiasi espressione che coinvolga un $VARIABLE
, almeno se si desidera che venga interpretato dalla shell come un unico elemento, altrimenti eventuali spazi nel contenuto di $VARIABLE
eliminerebbe il guscio.
Comprendo, tuttavia, che nelle versioni più recenti delle shell, la doppia virgoletta non è più sempre necessaria (almeno per lo scopo sopra descritto). Ad esempio, in 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
In zsh
, invece, gli stessi tre comandi riescono. Pertanto, sulla base di questo esperimento, sembra che, in bash
, si possono omettere le virgolette all'interno di [[ ... ]]
, ma non all'interno di [ ... ]
né negli argomenti della riga di comando, mentre in zsh
, le virgolette doppie possono essere omesse in tutti questi casi.
Ma dedurre regole generali da esempi aneddotici come quelli sopra è una proposta casuale. Sarebbe bello vedere un riepilogo di quando è necessaria la doppia virgoletta. Sono principalmente interessato a zsh
, bash
e /bin/sh
.
Risposta accettata:
Per prima cosa, separa zsh dal resto. Non è una questione di shell vecchie e moderne:zsh si comporta in modo diverso. I designer di zsh hanno deciso di renderlo incompatibile con le shell tradizionali (Bourne, ksh, bash), ma più facile da usare.
In secondo luogo, è molto più facile usare sempre le virgolette doppie che ricordare quando sono necessarie. Sono necessari la maggior parte del tempo, quindi dovrai imparare quando non sono necessari, non quando sono necessari.
In poche parole, le virgolette doppie sono necessarie ovunque sia previsto un elenco di parole o uno schema . Sono facoltativi nei contesti in cui il parser prevede una stringa grezza.
Cosa succede senza virgolette
Nota che senza virgolette accadono due cose.
- In primo luogo, il risultato dell'espansione (il valore della variabile per la sostituzione di un parametro come
${foo}
o l'output del comando per una sostituzione di comando come$(foo)
) viene suddiviso in parole ovunque contenga spazi bianchi.
Più precisamente, il risultato dell'espansione viene suddiviso in corrispondenza di ogni carattere che compare nel valore dell'IFS
variabile (carattere separatore). Se una sequenza di caratteri separatori contiene spazi bianchi (spazio, tabulazione o nuova riga), lo spazio bianco viene conteggiato come un singolo carattere; i separatori non di spazi vuoti iniziali, finali o ripetuti portano a campi vuoti. Ad esempio, conIFS=" :"
,:one::two : three: :four
produce campi vuoti prima dione
, traone
etwo
e (uno solo) trathree
efour
. - Ogni campo risultante dalla divisione viene interpretato come un glob (un modello di caratteri jolly) se contiene uno dei caratteri
[*?
. Se quel modello corrisponde a uno o più nomi di file, il modello viene sostituito dall'elenco dei nomi di file corrispondenti.
Un'espansione variabile senza virgolette $foo
è colloquialmente noto come "operatore split+glob", in contrasto con "$foo"
che prende solo il valore della variabile foo
. Lo stesso vale per la sostituzione del comando:"$(foo)"
è una sostituzione di comando, $(foo)
è una sostituzione di comando seguita da split+glob.
Dove puoi omettere le virgolette doppie
Ecco tutti i casi che mi vengono in mente in una shell in stile Bourne in cui puoi scrivere una variabile o una sostituzione di comando senza virgolette e il valore viene interpretato letteralmente.
-
Sul lato destro di un compito.
var=$stuff a_single_star=*
Nota che hai bisogno delle virgolette dopo
export
, perché è un normale builtin, non una parola chiave. Questo è vero solo in alcune shell come dash, zsh (nell'emulazione sh), yash o posh; bash e ksh trattano entrambiexport
specialmente.export VAR="$stuff"
-
In un
case
dichiarazione.case $var in …
Nota che hai bisogno di virgolette doppie in uno schema maiuscolo. La divisione delle parole non avviene in uno schema maiuscolo, ma una variabile senza virgolette viene interpretata come un modello mentre una variabile tra virgolette viene interpretata come una stringa letterale.
a_star='a*' case $var in "$a_star") echo "'$var' is the two characters a, *";; $a_star) echo "'$var' begins with a";; esac
-
Entro doppie parentesi. Le doppie parentesi sono una sintassi speciale della shell.
[[ -e $filename ]]
Tranne che hai bisogno di virgolette dove è previsto un modello o un'espressione regolare:sul lato destro di
=
o==
o!=
o=~
.a_star='a*' if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *" elif [[ $var == $a_star ]]; then echo "'$var' begins with a" fi
Hai bisogno di virgolette come al solito tra parentesi singole
[ … ]
perché sono una normale sintassi della shell (è un comando che si chiama[
). Vedi parentesi singole o doppie -
In un reindirizzamento in shell POSIX non interattive (non
bash
, néksh88
).echo "hello world" >$filename
Alcune shell, quando sono interattive, trattano il valore della variabile come un modello di caratteri jolly. POSIX proibisce questo comportamento in shell non interattive, ma alcune shell tra cui bash (tranne in modalità POSIX) e ksh88 (incluso quando trovato come (presumibilmente) POSIX
sh
di alcuni Unice commerciali come Solaris) lo fanno ancora lì (bash
tenta anche di dividere e il reindirizzamento non riesce a meno che dividi+globbing risulta esattamente in una parola), motivo per cui è meglio citare gli obiettivi dei reindirizzamenti in unsh
script nel caso tu voglia convertirlo in unbash
script un giorno o eseguirlo su un sistema in cuish
non è conforme a quel punto, oppure potrebbe essere proveniente da shell interattive. -
Dentro un'espressione aritmetica. In effetti, è necessario omettere le virgolette affinché una variabile venga analizzata come espressione aritmetica.
expr=2*2 echo "$(($expr))"
Tuttavia, hai bisogno delle virgolette attorno all'espansione aritmetica poiché sono soggette alla divisione delle parole nella maggior parte delle shell come richiede POSIX (!?).
-
In un pedice di matrice associativo.
typeset -A a i='foo bar*qux' a[foo bar*qux]=hello echo "${a[$i]}"
Una variabile senza virgolette e una sostituzione di comando possono essere utili in alcune rare circostanze:
- Quando il valore della variabile o l'output del comando consiste in un elenco di pattern glob e vuoi espandere questi pattern all'elenco dei file corrispondenti.
- Quando sai che il valore non contiene alcun carattere jolly, quel
$IFS
non è stato modificato e vuoi dividerlo in spazi bianchi. - Quando vuoi dividere un valore in un determinato carattere:disabilita il globbing con
set -f
, impostaIFS
al carattere separatore (o lascialo da solo per dividere in uno spazio bianco), quindi esegui l'espansione.
Zsh
In zsh, puoi omettere le virgolette la maggior parte delle volte, con poche eccezioni.
-
$var
non si espande mai in più parole, tuttavia si espande nell'elenco vuoto (al contrario di un elenco contenente una singola parola vuota) se il valore divar
è la stringa vuota. Contrasto:var= print -l $var foo # prints just foo print -l "$var" foo # prints an empty line, then foo
Allo stesso modo,
"${array[@]}"
si espande a tutti gli elementi dell'array, mentre$array
si espande solo agli elementi non vuoti. -
Il
@
il flag di espansione del parametro a volte richiede virgolette intorno all'intera sostituzione:"${(@)foo}"
. -
La sostituzione del comando subisce la suddivisione del campo se non tra virgolette:
echo $(echo 'a'; echo '*')
stampaa *
(con un solo spazio) mentreecho "$(echo 'a'; echo '*')"
stampa la stringa di due righe non modificata. Usa"$(somecommand)"
per ottenere l'output del comando in una sola parola, senza newline finali. Usa"${$(somecommand; echo _)%?}"
per ottenere l'output esatto del comando comprese le nuove righe finali. Usa"${(@f)$(somecommand)}"
per ottenere una matrice di righe dall'output del comando.