$ VAR vs $ {VAR} e per citare o non citare


Risposte:


99

VAR=$VAR1è una versione semplificata di VAR=${VAR1}. Ci sono cose che il secondo può fare che il primo non può, ad esempio fare riferimento a un indice di array (non portatile) o rimuovere una sottostringa (POSIX-portatile). Vedi la sezione Altro sulle variabili della Guida di Bash per principianti ed espansione dei parametri nelle specifiche POSIX.

Usare le virgolette attorno a una variabile come in rm -- "$VAR1"o rm -- "${VAR}"è una buona idea. Questo rende il contenuto della variabile un'unità atomica. Se il valore della variabile contiene spazi vuoti (beh, caratteri nella $IFSvariabile speciale, spazi vuoti per impostazione predefinita) o caratteri globbing e non li citate, allora ogni parola viene considerata per la generazione del nome file (globbing) la cui espansione rende altrettanti argomenti per qualsiasi cosa tu stai facendo.

$ find .
.
./*r*
./-rf
./another
./filename
./spaced filename
./another spaced filename
./another spaced filename/x
$ var='spaced filename'
# usually, 'spaced filename' would come from the output of some command and you weren't expecting it
$ rm $var
rm: cannot remove 'spaced': No such file or directory
# oops! I just ran 'rm spaced filename'
$ var='*r*'
$ rm $var
# expands to: 'rm' '-rf' '*r*' 'another spaced filename'

$ find .
.
./another
./spaced filename
./another spaced filename
$ var='another spaced filename'
$ rm -- "$var"
$ find .
.
./another
./spaced filename

Sulla portabilità: secondo POSIX.1-2008 sezione 2.6.2 , le parentesi graffe sono opzionali.


@shawn ha aggiornato la mia domanda perché sono anche curioso della portabilità
xenoterracide

@shawn: dubito che il tuo esempio sia valido. Hai qualche vero esempio di shell in cui l' var1=$varespansione genera un errore?
alex

@alex: grazie. Pensavo di averlo provato sulla riga di comando, ma l'ho fatto nel modo sbagliato. Ho cambiato l'esempio.
Shawn J. Goff,

Con l'esempio aggiornato è meglio tenere presente che in genere si dovrebbe desiderare la versione citata, poiché l'esempio è piuttosto un caso angolare.
alex

9
@Shawn: le virgolette non sono necessarie in un compito. Sono necessari nella maggior parte degli altri usi, incluso export VAR=$VAR1. Per quanto riguarda le parentesi graffe, sono opzionali (controlla il quarto paragrafo della sezione che hai citato; questo è il caso in tutte le shell pre-POSIX e POSIX).
Gilles,

61

${VAR}e $VARsono esattamente equivalenti. Per una semplice espansione variabile, l'unico motivo da utilizzare ${VAR}è quando l'analisi analizzerebbe altrimenti troppi caratteri nel nome della variabile, come in ${VAR1}_$VAR2(a cui senza parentesi sarebbe equivalente ${VAR1_}$VAR2). Espansioni più ornati ( ${VAR:=default}, ${VAR#prefix}, ...) richiedono parentesi graffe.

In un'assegnazione variabile, la divisione del campo (ovvero la divisione nello spazio bianco nel valore) e l' espansione del nome del percorso (ovvero il globbing) sono disattivate, quindi VAR=$VAR1è esattamente equivalente a VAR="$VAR1", in tutte le shell POSIX e in tutte le sh pre-POSIX di cui ho sentito parlare . (Rif POSIX: comandi semplici ). Per lo stesso motivo, si VAR=*imposta in modo affidabile VARsulla stringa letterale *; ovviamente si VAR=a bimposta VARsu apoiché bin primo luogo è una parola separata. In generale, le virgolette doppie non sono necessarie quando la sintassi della shell prevede una sola parola, ad esempio incase … in (ma non nel modello), ma anche lì è necessario fare attenzione: ad esempio POSIX specifica chei target di reindirizzamento ( >$filename) non richiedono virgolette negli script, ma alcune shell tra cui bash richiedono le doppie virgolette anche negli script. Vedi Quando è necessaria la doppia citazione? per un'analisi più approfondita.

In altri casi sono necessarie le doppie virgolette, in particolare in export VAR="${VAR1}"(che può essere equivalentemente scritto export "VAR=${VAR1}") in molte shell (POSIX lascia questo caso aperto). La somiglianza di questo caso con compiti semplici e la natura sparsa dell'elenco dei casi in cui non hai bisogno di virgolette doppie, sono il motivo per cui ti consiglio di usare solo virgolette doppie a meno che tu non voglia dividere e glob.


2
Come regola generale, citerò sempre le espansioni variabili anche quando so che il valore non conterrà alcun IFScarattere perché voglio avere l'abitudine. L'unica eccezione è che non cito il valore quando si esegue un'assegnazione variabile (a meno che non sia richiesto, ad esempio quando il valore contiene uno spazio). Questo rende l'evidenziazione della sintassi dell'editor più utile quando ci sono sostituzioni di comandi come FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}"). Invece di colorare tutto il colore "stringa", ottengo l'evidenziazione della sintassi del codice nidificato. Questo è anche il motivo per cui evito i backtick.
Richard Hansen,

Sebbene >$filesia OK negli script POSIX, non è in bash anche quando non interattivo (a meno che la conformità POSIX sia imposta con $POSIXLY_CORRECTo --posix...).
Stéphane Chazelas,

Anche se è vero che le citazioni non sono necessarie VAR=$VAR1, sono stato sorpreso a volte local VAR=$VAR1, che ricordo di aver lavorato in modo diverso sotto alcuni aspetti, almeno in alcune conchiglie. Ma atm, non riesco a riprodurre la divergenza.
dubiousjim,

Ok, ho trovato il problema che stavo ricordando . Si presenta solo in alcune conchiglie.
dubiousjim,

@dubiousjim local VAR=$VAR1è come export VAR=$VAR1, dipende dalla shell.
Gilles,

8

Quotazione

Si consideri che la doppia virgoletta viene utilizzata per l'espansione variabile e la virgoletta singola viene utilizzata per le virgolette forti, ovvero l'espansione sans.

Espansione:

this='foo'
that='bar'
these="$this"
those='$that'

Produzione:

for item in "$this" "$that" "$these" "$those"; do echo "$item"; done
foo
bar
foo
$that

Potrebbe essere utile menzionare che è necessario utilizzare la quotazione laddove possibile per diversi motivi, tra i quali i migliori sono considerati best practice e leggibilità. Anche perché Bash a volte è bizzarro e spesso per modi apparentemente illogici o irragionevoli / imprevisti, e la quotazione cambia le aspettative implicite in esplicite, il che riduce quella superficie di errore (o potenziale per ciò).

E mentre è completamente legale non quotare, e funzionerà nella maggior parte dei casi, tale funzionalità è fornita per comodità ed è probabilmente meno portatile. la pratica completamente formale garantita per riflettere l'intento e le aspettative è di citare.

Sostituzione

Ora considera anche che il costrutto "${somevar}"è usato per le operazioni di sostituzione. Diversi casi d'uso, come la sostituzione e le matrici.

Sostituzione (stripping):

thisfile='foobar.txt.bak'
foo="${thisfile%.*}"   # removes shortest part of value in $thisfile matching after '%' from righthand side
bar="${thisfile%%.*}"  # removes longest matching

for item in "$foo" "$bar"; do echo "$item"; done
foobar.txt
foobar

Sostituzione (sostituzione):

foobar='Simplest, least effective, least powerful'
# ${var/find/replace_with}
foo="${foobar/least/most}"   #single occurrence
bar="${foobar//least/most}"  #global occurrence (all)

for item in "$foobar" "$foo" "$bar"; do echo "$item"; done
Simplest, least effective, least powerful
Simplest, most effective, least powerful
Simplest, most effective, most powerful

Array:

mkdir temp
# create files foo.txt, bar.txt, foobar.txt in temp folder
touch temp/{foo,bar,foobar}.txt
# alpha is array of output from ls  
alpha=($(ls temp/*))

echo "$alpha"         #  temp/foo.txt
echo "${alpha}"       #  temp/foo.txt
echo "${alpha[@]}"    #  temp/bar.txt  temp/foobar.txt  temp/foo.txt
echo "${#alpha}"      #  12 # length of first element (implicit index [0])
echo "${#alpha[@]}"   #  3  # number of elements
echo "${alpha[1]}"    #  temp/foobar.txt # second element
echo "${#alpha[1])"   #  15 # length of second element

for item in "${alpha[@]}"; do echo "$item"; done
temp/bar.txt
temp/foobar.txt
temp/foo.txt

Tutto questo a malapena graffia la superficie del "${var}"costrutto di sostituzione. Il riferimento definitivo per gli script di shell Bash è il riferimento online gratuito, TLDP The Linux Documentation Projecthttps://www.tldp.org/LDP/abs/html/parameter-substitution.html


1
molto informativo.
Orion Elenzil,

0
ls -la

lrwxrwxrwx.  1 root root      31 Nov 17 13:13 prodhostname
lrwxrwxrwx.  1 root root      33 Nov 17 13:13 testhostname
lrwxrwxrwx.  1 root root      32 Nov 17 13:13 justname

fine quindi:

env=$1
    if [ ! -f /dirname/${env}hostname ]

vale la pena ricordare come esempio più chiaro di utilizzo dei ricci

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.