GNU/Linux >> Linux Esercitazione >  >> Linux

Quando avvolgere le virgolette attorno a una variabile di shell?

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?

Linux
  1. Come impostare la tua variabile $PATH in Linux

  2. Problema con la variabile della shell durante il tentativo di mkdir

  3. Come preservare le interruzioni di riga durante la memorizzazione dell'output del comando in una variabile?

  4. Problemi con la variabile bash che si espande con virgolette singole

  5. Come posso avviare una seconda shell in modalità utente singolo?

Come memorizzare un comando Linux come variabile nello script della shell

Perché una variabile è visibile in una subshell?

Espansione di una variabile shell ed effetto di glob e split su di essa?

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

Spiegazione del comando di esportazione in Linux

Che cos'è Subshell in Linux?