$ VAR vs $ {VAR} et citer ou ne pas citer

140

je peux écrire

VAR=$VAR1
VAR=${VAR1}
VAR="$VAR1"
VAR="${VAR1}"

le résultat final me semble à peu près le même. Pourquoi devrais-je écrire l'un ou l'autre? y en a-t-il qui ne sont pas portables / POSIX?

xénoterracide
la source

Réponses:

100

VAR=$VAR1est une version simplifiée de VAR=${VAR1}. La seconde peut faire certaines choses que la première ne peut pas faire, par exemple référencer un index de tableau (non portable) ou supprimer une sous-chaîne (POSIX-portable). Voir la section Plus d'informations sur les variables du Guide Bash pour les débutants et le développement de paramètres dans les spécifications POSIX.

L'utilisation de guillemets autour d'une variable telle que rm -- "$VAR1"ou rm -- "${VAR}"est une bonne idée. Cela fait du contenu de la variable une unité atomique. Si la valeur de la variable contient des blancs (ainsi, des caractères dans la $IFSvariable spéciale, des blancs par défaut) ou des caractères globaux et que vous ne le citez pas, alors chaque mot est pris en compte pour la génération du nom de fichier (globbing) dont l'expansion multiplie autant d'arguments que vous le souhaitez. faites-vous.

$ 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

Sur la portabilité: Selon POSIX.1-2008 section 2.6.2 , les accolades sont facultatives.

Shawn J. Goff
la source
@shawn a mis à jour ma question parce que je suis également curieux de savoir
s'il est portable
@shawn: je doute que votre exemple soit valide. Avez-vous un exemple réel de shell où l' var1=$varexpansion donne une erreur?
alex
@alex: Merci. Je pensais avoir testé cela sur la ligne de commande, mais je l’ai mal fait. J'ai changé l'exemple.
Shawn J. Goff
Avec l'exemple mis à jour, il est préférable de garder à l'esprit que vous devriez généralement vouloir la version citée, car l'exemple est plutôt un cas de coin.
alex
9
@Shawn: Les guillemets ne sont pas nécessaires dans une affectation. Ils sont nécessaires dans la plupart des autres utilisations, y compris export VAR=$VAR1. Quant aux accolades, elles sont facultatives (consultez le quatrième paragraphe de la section que vous avez citée; c'est le cas de tous les shells antérieurs à POSIX et POSIX).
Gilles
61

${VAR}et $VARsont exactement équivalents. Pour un développement de variable simple, la seule raison à utiliser ${VAR}est le moment où l'analyse capturerait trop de caractères dans le nom de la variable, comme dans ${VAR1}_$VAR2(qui, sans accolades, équivaudrait à ${VAR1_}$VAR2). La plupart des extensions (parées ${VAR:=default}, ${VAR#prefix}...) nécessitent des accolades.

Dans une affectation de variable, le fractionnement du champ ( par exemple le fractionnement à des espaces de la valeur) et l' expansion du chemin (c. -à- englobement) sont désactivés, donc VAR=$VAR1est exactement équivalente à VAR="$VAR1", dans toutes les coquilles POSIX et dans tous les poissons pré-Posix que je l' ai entendu parler de . (Réf. POSIX: commandes simples ). Pour la même raison, VAR=*définit de manière fiable VARla chaîne littérale *; bien sûr VAR=a bmet VARà apuisque le best un mot séparé en premier lieu. En règle générale, les guillemets doubles ne sont pas nécessaires lorsque la syntaxe du shell attend un seul mot, par exemple, danscase … in (mais pas dans le modèle), mais même là, vous devez faire attention: par exemple, POSIX spécifie queLes cibles de redirection ( >$filename) ne nécessitent pas de guillemets dans les scripts, mais quelques shells, y compris bash, requièrent les guillemets doubles, même dans les scripts. Voir Quand la double cotation est-elle nécessaire? pour une analyse plus approfondie.

Vous avez besoin des guillemets dans d'autres cas, en particulier dans export VAR="${VAR1}"(ce qui peut être écrit de manière équivalente export "VAR=${VAR1}") dans de nombreux shells (POSIX laisse ce cas ouvert). La similitude de ce cas avec des affectations simples et la nature dispersée de la liste des cas dans lesquels vous n’avez pas besoin de guillemets, sont la raison pour laquelle je recommande d’utiliser des guillemets doubles à moins que vous ne souhaitiez scinder et glob.

Gilles
la source
2
En règle générale, je citerai toujours les développements variables, même si je sais que la valeur ne contiendra aucun IFScaractère, car je veux en prendre l'habitude. La seule exception est que je ne cite pas la valeur lors d'une affectation de variable (sauf si requis, comme lorsque la valeur contient un espace). Cela rend la mise en évidence de la syntaxe de l'éditeur plus utile lorsqu'il existe des substitutions de commandes telles que FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}"). Plutôt que de tout colorer la couleur "chaîne", j'obtiens la coloration syntaxique du code imbriqué. C'est aussi pourquoi j'évite les backticks.
Richard Hansen
While >$fileest OK dans les scripts POSIX, il n'est pas dans bash même s'il n'est pas interactif (sauf si la conformité POSIX est appliquée avec $POSIXLY_CORRECTou --posix...).
Stéphane Chazelas
S'il est vrai que les citations ne sont pas nécessaires VAR=$VAR1, j'ai parfois été surpris par le fait local VAR=$VAR1que je me souviens d'avoir travaillé différemment à certains égards, au moins dans certains obus. Mais atm, je ne peux pas reproduire la divergence.
dubiousjim
Ok, j'ai trouvé le problème dont je me souvenais . Cela ne se voit que dans certains coquillages.
dubiousjim
@dubiousjim local VAR=$VAR1est comme export VAR=$VAR1, cela dépend de la coquille.
Gilles
8

Citation

Considérez que les guillemets doubles sont utilisés pour les extensions variables et les guillemets simples pour les guillemets forts, c'est-à-dire sans expansion.

Expansion:

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

Sortie:

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

Il peut être intéressant de mentionner que vous devez utiliser la citation chaque fois que possible pour plusieurs raisons, dont la meilleure est qu’elle est considérée comme une pratique exemplaire et par souci de lisibilité. Aussi, parce que Bash est parfois bizarre et souvent pour des manières apparemment illogiques ou déraisonnables / inattendues, et que la citation modifie les attentes implicites en explicites, ce qui réduit cette surface d'erreur (ou son potentiel).

Et bien qu'il soit tout à fait légal de ne pas citer, et que cela fonctionne dans la plupart des cas, cette fonctionnalité est fournie pour plus de commodité et est probablement moins portable. la pratique entièrement formelle qui garantit de refléter l'intention et les attentes consiste à citer.

Substitution

Considérons maintenant que la construction "${somevar}"est utilisée pour les opérations de substitution. Plusieurs cas d'utilisation, tels que le remplacement et les tableaux.

Remplacement (décapage):

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

Remplacement (remplacement):

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

Tableaux:

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

Tout cela ne fait qu’effleurer la surface de la "${var}"construction de substitution. La référence définitive pour les scripts shell Bash est la référence en ligne libre, TLDP The Linux Documentation Projecthttps://www.tldp.org/LDP/abs/html/parameter-substitution.html

SYANiDE
la source
1
très instructif.
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

fin ensuite:

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

mérite d'être cité comme exemple plus clair d'utilisation de curlies

ninjabber
la source