Quando racchiudere le virgolette attorno a una variabile di shell?

Qualcuno potrebbe dirmi se dovrei incastrare le virgolette sulle variabili in uno script di shell?

Ad esempio, è il seguente corretto:

xdg-open $URL [ $? -eq 2 ] 

o

 xdg-open "$URL" [ "$?" -eq "2" ] 

E se sì, perché?

Regola generale: citala se può essere vuota o contenere spazi (o qualsiasi spazio bianco) o caratteri speciali (caratteri jolly). Non citare stringhe con spazi porta spesso alla shell a rompere un singolo argomento in molti.

$? non ha bisogno di virgolette dato che è un valore numerico. Se l’ $URL bisogno dipende da ciò che permetti 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 divisione dei token e l’espansione dei caratteri jolly.

Le virgolette singole proteggono letteralmente il testo tra loro. È lo strumento adatto quando è necessario assicurarsi che la shell non tocchi affatto la stringa. In genere, è il meccanismo di quoting di scelta quando non si richiede l’interpolazione variabile.

 $ 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 doppie virgolette sono adatte quando è richiesta l’interpolazione variabile. Con adattamenti adatti, è anche una buona soluzione quando hai bisogno di virgolette singole nella stringa. (Non esiste un modo semplice per sfuggire a una singola virgoletta tra virgolette singole, perché non esiste un meccanismo di fuga all’interno di virgolette singole; in caso contrario, non citerebbero completamente letteralmente.)

 $ echo "There is no place like '$HOME'" There is no place like '/home/me' 

Nessuna virgolette è adatta quando si richiede specificamente alla shell di eseguire la suddivisione di token e / o l’espansione di caratteri jolly.

Scissione di 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 una volta sola, sopra la stringa singola, quotata).

  $ for word in '$words'; do echo "$word"; done $words 

(Il ciclo viene eseguito solo una volta, sopra la stringa letterale con quotatura singola.)

Espansione 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 esiste un file chiamato letteralmente file*.txt .)

 $ ls '$pattern' ls: cannot access $pattern: No such file or directory 

(Non esiste neanche un file chiamato $pattern !)

In termini più concreti, qualsiasi cosa che contenga un nome di file dovrebbe di solito essere citata (perché i nomi di file possono contenere spazi bianchi e altri metacaratteri della shell). Qualunque cosa contenga un URL dovrebbe di solito essere quotata (perché molti URL contengono metacaratteri della shell come ? E & ). Qualunque cosa contenga una regex dovrebbe di solito essere citata (idem come idem). Tutto ciò che contiene spazi bianchi significativi diversi da spazi singoli tra personaggi non bianchi deve essere citato (perché altrimenti, la shell munge gli spazi bianchi in spazi effettivamente, singoli, e taglia ogni spazio bianco iniziale o finale).

Quando sai che una variabile può contenere solo un valore che non contiene metacaratteri della shell, la citazione è facoltativa. Quindi, un $? non quotato $? è fondamentalmente buono, perché questa variabile può contenere solo un numero. Tuttavia, "$?" è anche corretto e raccomandato per coerenza generale e correttezza (sebbene questa sia la mia raccomandazione personale, non una politica ampiamente riconosciuta).

I valori che non sono variabili seguono fondamentalmente le stesse regole, anche se potresti anche sfuggire ai metacaratteri invece di quotarli. Per un esempio comune, un URL con un & in esso verrà analizzato dalla shell come comando di sfondo a meno che il metacarattere non sia in escape o tra virgolette:

 $ wget http://example.com/q&uack [1] wget http://example.com/q -bash: uack: command not found 

(Naturalmente, questo succede anche se l’URL è in una variabile non quotata.) Per una stringa statica, le virgolette singole hanno più senso, anche se qualsiasi forma di citazione o di escaping funziona qui.

 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 definire “quotazione altalena”. Se è necessario combinare virgolette singole e doppie, è ansible utilizzarle adiacenti l’una all’altra. Ad esempio, le seguenti stringhe citate

 '$HOME ' "isn't" ' where `<3' "' is." 

può essere incollato insieme di nuovo in retro, formando una singola stringa lunga dopo la tokenizzazione e la rimozione delle quote.

 $ echo '$HOME '"isn't"' where `<3'"' is." $HOME isn't where `<3' is. 

Questo non è terribilmente leggibile, ma è una tecnica comune e quindi utile da conoscere.

Per inciso, gli script di solito non usano ls per niente. Per espandere un jolly, basta ... usarlo.

 $ 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 nel secondo esempio: printf funziona in modo specifico con più argomenti, anche stat 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 è vista meno frequentemente, quindi a volte abbreviamo "cita tutto a meno che tu non sappia esattamente cosa stai facendo".

Ecco una formula a tre punti per le virgolette in generale:

Virgolette

In contesti in cui vogliamo sopprimere la divisione delle parole e il globbing. Anche in contesti in cui vogliamo che il letterale sia trattato come una stringa, non una regex.

Virgolette singole

In stringhe letterali in cui vogliamo sopprimere l’interpolazione e il trattamento speciale dei backslash. In altre parole, le situazioni in cui l’utilizzo di virgolette doppie sarebbe inappropriato.

Nessuna citazione

In contesti in cui siamo assolutamente sicuri che non ci siano problemi di divisione delle parole o di globbing o che vogliamo dividere le parole e diventare globbing .


Esempi

Virgolette

  • stringhe letterali con spazi bianchi ( "StackOverflow rocks!" , "Steve's Apple" )
  • espansioni variabili ( "$var" , "${arr[@]}" )
  • sostituzioni di comando ( "$(ls)" , "`ls`" )
  • glob in cui 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

  • comando nomi e argomenti che hanno spazi bianchi in essi
  • stringhe letterali che richiedono l’interpolazione per essere soppresse ( 'Really costs $$!' , 'just a backslash followed by at: \t' )
  • per proteggere le virgolette doppie ( 'The "crux"' )
  • regex letterali che richiedono l’interpolazione per essere soppressi
  • usa la quotatura della shell per i letterali che coinvolgono caratteri speciali ( $'\n\t' )
  • usa la quotatura della shell dove dobbiamo proteggere diverse virgolette singole e doppie ( $'{"table": "users", "where": "first_name"=\'Steve\'}' )

Nessuna citazione

  • attorno a variabili numeriche standard ( $$ , $? $# ecc.)
  • in contesti aritmetici come ((count++)) , "${arr[idx]}" , "${string:start:length}"
  • all’interno [[ ]] espressione che è libera da problemi di divisione delle parole e di globbing (questa è una questione di stile e le opinioni possono variare ampiamente)
  • dove vogliamo la divisione delle parole ( for word in $words )
  • dove vogliamo globbing ( for txtfile in *.txt; do ... )
  • dove vogliamo ~ essere interpretato come $HOME ( ~/"some dir" ma non "~/some dir" )

Guarda anche:

  • Differenza tra virgolette singole e doppie in Bash
  • Quali sono le variabili speciali della shell del simbolo del dollaro?

Di solito uso quotati come "$var" per sicurezza, a meno che non sia sicuro che $var non contenga spazio.

Io uso $var come un modo semplice per unire le linee:

 lines="`cat multi-lines-text-file.txt`" echo "$lines" ## multiple lines echo $lines ## all spaces (including newlines) are zapped