GNU/Linux >> Linux Esercitazione >  >> Linux

Quando è necessaria la doppia virgoletta?

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.

  1. 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, con IFS=" :" , :one::two : three: :four  produce campi vuoti prima di one , tra one e two e (uno solo) tra three e four .
  2. 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 entrambi export 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 un sh script nel caso tu voglia convertirlo in un bash script un giorno o eseguirlo su un sistema in cui sh 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 , imposta IFS al carattere separatore (o lascialo da solo per dividere in uno spazio bianco), quindi esegui l'espansione.
Correlati:perché utilizzare lo scambio quando c'è spazio libero più che sufficiente nella RAM?

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 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, "${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 '*') stampa a * (con un solo spazio) mentre echo "$(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.

Correlati:typeset -A sta dando un errore nello script?
Linux
  1. Perché The Tilde (~) non si espande all'interno di virgolette doppie?

  2. Caratteri jolly all'interno delle citazioni?

  3. Avvolgere un comando che include virgolette singole e doppie per un altro comando?

  4. Quando usare Nohup?

  5. Qual è un buon mnemonico per Shell Double vs. Citazioni singole?

Quando utilizzare un server dedicato

Quando avvolgere le virgolette attorno a una variabile di shell?

Aggiungi virgolette doppie attorno ai campi nell'output dello script AWK?

comando bash alias con virgolette singole e doppie

come rimuovere le virgolette doppie in un csv

Sostituzione del trattino basso con virgola e rimozione delle virgolette doppie in CSV