Quando è necessaria la doppia citazione?


120

Il vecchio consiglio era di citare due volte qualsiasi espressione che coinvolge un $VARIABLE, almeno se si voleva che fosse interpretato dalla shell come un singolo elemento, altrimenti qualsiasi spazio nel contenuto di $VARIABLEavrebbe gettato via la shell.

Capisco, tuttavia, che nelle versioni più recenti delle shell, la doppia virgoletta non è più 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 hanno successo. Pertanto, sulla base di questo esperimento, sembra che, in bash, si possano omettere le doppie virgolette all'interno [[ ... ]], ma non all'interno [ ... ]né negli argomenti della riga di comando, mentre, in zsh, le doppie virgolette possono essere omesse in tutti questi casi.

Ma inferire regole generali da esempi aneddotici come sopra è una proposizione casuale. Sarebbe bello vedere un riepilogo di quando è necessaria la doppia citazione. Sono principalmente interessato a zsh, bashe /bin/sh.


10
Il comportamento osservato in zsh dipende dalle impostazioni ed è influenzato SH_WORD_SPLITdall'opzione.
Ulrich Dangel,


3
A parte questo, i nomi delle variabili in maiuscolo sono usati dalle variabili con significato per il sistema operativo e la shell; la specifica POSIX consiglia esplicitamente di utilizzare nomi minuscoli per le variabili definite dall'applicazione. (Mentre la specifica citata si concentra in particolare sulle variabili di ambiente, le variabili di ambiente e le variabili di shell condividono uno spazio dei nomi: il tentativo di creare una variabile di shell con un nome già utilizzato da una variabile di ambiente sovrascrive quest'ultimo). Vedi pubs.opengroup.org/onlinepubs/009695399/basedefs/… , quarto paragrafo.
Charles Duffy,

Risposte:


128

Innanzitutto, separa zsh dal resto. Non è una questione di shell vecchie vs moderne: zsh si comporta in modo diverso. I progettisti 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 le doppie virgolette continuamente che ricordare quando sono necessarie. Sono necessari per la maggior parte del tempo, quindi dovrai imparare quando non sono necessari, non quando sono necessari.

In breve, sono necessarie doppie virgolette ovunque sia previsto un elenco di parole o uno schema . Sono facoltativi in ​​contesti in cui il parser prevede una stringa non elaborata.

Cosa succede senza virgolette

Si noti che senza virgolette doppie, accadono due cose.

  1. Innanzitutto, il risultato dell'espansione (il valore della variabile per una sostituzione di parametro simile ${foo}o l'output del comando per una sostituzione di comando simile $(foo)) viene suddiviso in parole ovunque contenga spazi bianchi.
    Più precisamente, il risultato dell'espansione è diviso per ogni carattere che appare nel valore della IFSvariabile (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 iniziali, finali o ripetuti non bianchi generano campi vuoti. Ad esempio, con IFS=" :", :one::two : three: :four produce campi vuoti prima one, tra onee twoe (uno solo) tra threee four.
  2. Ogni campo risultante dalla divisione viene interpretato come un glob (un modello 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 non quotata $fooè colloquialmente nota come "operatore split + glob", al contrario della "$foo"quale prende semplicemente il valore della variabile foo. Lo stesso vale per la sostituzione dei comandi: "$(foo)"è una sostituzione dei comandi, $(foo)è una sostituzione dei comandi seguita da split + glob.

Dove puoi omettere le doppie virgolette

Ecco tutti i casi che mi vengono in mente in una shell in stile Bourne in cui è possibile scrivere una variabile o sostituire i comandi senza virgolette doppie e il valore viene interpretato letteralmente.

  • Sul lato destro di un compito.

    var=$stuff
    a_single_star=*

    Nota che dopo hai bisogno delle doppie virgolette export, perché è un normale built-in, non una parola chiave. Questo è vero solo in alcune shell come dash, zsh (in emulazione sh), yash o posh; bash e ksh trattano entrambi in modo exportspeciale.

    export VAR="$stuff"
  • In una casedichiarazione.

    case $var in 

    Nota che hai bisogno di virgolette doppie in un modello di caso. La suddivisione delle parole non avviene in un caso, ma una variabile non quotata viene interpretata come un motivo, 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
  • Tra doppie parentesi. Le parentesi doppie sono una sintassi speciale della shell.

    [[ -e $filename ]]

    Tranne che hai bisogno di virgolette doppie 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 doppie come al solito tra parentesi singole [ … ]perché sono normali sintassi della shell (è un comando che sembra essere chiamato [). Vedi parentesi singole o doppie

  • In un reindirizzamento in shell POSIX non interattive (no bash, né ksh88).

    echo "hello world" >$filename

    Alcune shell, quando interattive, trattano il valore della variabile come un modello jolly. POSIX proibisce quel comportamento in shell non interattive, ma alcune shell tra cui bash (tranne che in modalità POSIX) e ksh88 (incluso quando trovato come POSIX (presumibilmente) shdi alcuni Unici commerciali come Solaris) lo fanno ancora lì ( bashtenta anche di dividere e il reindirizzamento ha esito negativo a meno che tale divisione + globbing non comporti esattamente una parola), motivo per cui è meglio citare gli obiettivi dei reindirizzamenti in uno shscript nel caso in cui si desideri convertirlo in uno bashscript un giorno o eseguirlo su un sistema in cui si shtrova non conformi su questo punto, o può essere di provenienza da shell interattive.

  • All'interno di un'espressione aritmetica. In effetti, è necessario escludere le virgolette affinché una variabile venga analizzata come espressione aritmetica.

    expr=2*2
    echo "$(($expr))"

    Tuttavia, sono necessarie le virgolette intorno all'espansione aritmetica in quanto sono soggette alla suddivisione delle parole nella maggior parte delle shell come richiede POSIX (!?).

  • In un indice associativo dell'array.

    typeset -A a
    i='foo bar*qux'
    a[foo\ bar\*qux]=hello
    echo "${a[$i]}"

Una variabile non quotata e la sostituzione dei comandi possono essere utili in alcune rare circostanze:

  • Quando il valore della variabile o l'output del comando è costituito da un elenco di modelli glob e si desidera espandere tali modelli nell'elenco dei file corrispondenti.
  • Quando sai che il valore non contiene alcun carattere jolly, questo $IFSnon è stato modificato e vuoi dividerlo in spazi bianchi.
  • Quando vuoi dividere un valore per un certo personaggio: disabilita il globbing con set -f, imposta IFSil carattere separatore (o lascialo solo per dividere in spazi bianchi), quindi fai l'espansione.

zsh

In zsh, puoi omettere le doppie virgolette il più delle volte, con alcune eccezioni.

  • $varnon si espande mai in più parole, tuttavia si espande nell'elenco vuoto (al contrario di un elenco contenente una sola parola vuota) se il valore di varè 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, si "${array[@]}"espande a tutti gli elementi dell'array, mentre $arraysi espande solo agli elementi non vuoti.

  • La @bandiera espansione di parametro a volte richiede dei doppi apici l'intera sostituzione: "${(@)foo}".

  • La sostituzione dei comandi viene suddivisa in campi se non quotata: echo $(echo 'a'; echo '*')stampa a *(con un solo spazio) mentre echo "$(echo 'a'; echo '*')"stampa la stringa a due righe non modificata. Utilizzare "$(somecommand)"per ottenere l'output del comando in una sola parola, senza newline finali. Utilizzare "${$(somecommand; echo _)%?}"per ottenere l'output esatto del comando, compresi i newline finali. Utilizzare "${(@f)$(somecommand)}"per ottenere una matrice di linee dall'output del comando.


In effetti, è necessario escludere le virgolette affinché una variabile venga analizzata come espressione aritmetica. Perché sono in grado di far funzionare il tuo esempio con le virgolette:echo "$(("$expr"))"
Cyker,

Questo è ciò che man bashdice: l'espressione viene trattata come se fosse racchiusa tra virgolette doppie, ma una virgoletta doppia tra parentesi non viene trattata in modo speciale.
Cyker,

4
Inoltre, per chiunque sia interessato, i nomi formali di split + glob sono suddivisione delle parole ed espansione del percorso .
Cyker,

3
Cordiali saluti - su StackOverflow , ho avuto qualcuno che estrae il linguaggio "facoltativo quando è prevista una stringa non elaborata" in questa risposta per difendere non citando un argomento echo. Potrebbe valere la pena provare a rendere la lingua ancora più esplicita ("forse quando il parser si aspetta una stringa non elaborata", forse?)
Charles Duffy

2
@CharlesDuffy Ugh, non avevo pensato a questa lettura errata. Ho cambiato "dove" in "quando" e rafforzato la frase come mi hai suggerito.
Gilles
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.