Quelle est la différence entre $ {var}, "$ var" et "$ {var}" dans le shell Bash?

135

Ce que le titre dit: qu'est-ce que cela signifie pour encapsuler une variable {}, ""ou "{}? » Je ne l' ai pas été en mesure de trouver des explications en ligne à ce sujet - je ne l' ai pas été en mesure de se référer à eux , sauf pour l' utilisation des symboles, ne donne rien.

Voici un exemple:

declare -a groups

groups+=("CN=exampleexample,OU=exampleexample,OU=exampleexample,DC=example,DC=com")
groups+=("CN=example example,OU=example example,OU=example example,DC=example,DC=com")

Ce:

for group in "${groups[@]}"; do
    echo $group
done

Se révèle très différent de celui-ci:

for group in $groups; do
    echo $group
done

et ça:

for group in ${groups}; do
    echo $group
done

Seul le premier accomplit ce que je veux: parcourir chaque élément du tableau. Je ne suis pas vraiment clair sur les différences entre $groups, "$groups", ${groups}et "${groups}". Si quelqu'un pouvait l'expliquer, je l'apprécierais.

Comme question supplémentaire - est-ce que quelqu'un connaît la manière acceptée de se référer à ces encapsulations?

SheerSt
la source

Réponses:

229

Bretelles ( $varvs. ${var})

Dans la plupart des cas, $varet ${var}sont les mêmes:

var=foo
echo $var
# foo
echo ${var}
# foo

Les accolades ne sont nécessaires que pour résoudre l'ambiguïté dans les expressions:

var=foo
echo $varbar
# Prints nothing because there is no variable 'varbar'
echo ${var}bar
# foobar

Citations ( $varvs. "$var"vs. "${var}")

Lorsque vous ajoutez des guillemets doubles autour d'une variable, vous dites au shell de la traiter comme un seul mot, même si elle contient des espaces:

var="foo bar"
for i in "$var"; do # Expands to 'for i in "foo bar"; do...'
    echo $i         #   so only runs the loop once
done
# foo bar

Comparez ce comportement avec ce qui suit:

var="foo bar"
for i in $var; do # Expands to 'for i in foo bar; do...'
    echo $i       #   so runs the loop twice, once for each argument
done
# foo
# bar

Comme avec $varvs. ${var}, les accolades ne sont nécessaires que pour lever l'ambiguïté, par exemple:

var="foo bar"
for i in "$varbar"; do # Expands to 'for i in ""; do...' since there is no
    echo $i            #   variable named 'varbar', so loop runs once and
done                   #   prints nothing (actually "")

var="foo bar"
for i in "${var}bar"; do # Expands to 'for i in "foo barbar"; do...'
    echo $i              #   so runs the loop once
done
# foo barbar

Notez que "${var}bar"dans le deuxième exemple ci-dessus pourrait également être écrit "${var}"bar, auquel cas vous n'avez plus besoin des accolades, c'est-à-dire "$var"bar. Cependant, si vous avez beaucoup de guillemets dans votre chaîne, ces formes alternatives peuvent devenir difficiles à lire (et donc à maintenir). Cette page fournit une bonne introduction à la citation dans Bash.

Tableaux ( $varvs. $var[@]vs. ${var[@]})

Maintenant pour votre tableau. Selon le manuel bash :

Référencer une variable de tableau sans indice équivaut à référencer le tableau avec un indice de 0.

En d'autres termes, si vous ne fournissez pas d'index avec [], vous obtenez le premier élément du tableau:

foo=(a b c)
echo $foo
# a

Ce qui est exactement le même que

foo=(a b c)
echo ${foo}
# a

Pour obtenir tous les éléments d'un tableau, vous devez utiliser @comme index, par exemple ${foo[@]}. Les accolades sont obligatoires avec les tableaux car sans eux, le shell développerait d'abord la $foopartie, donnant le premier élément du tableau suivi d'un littéral [@]:

foo=(a b c)
echo ${foo[@]}
# a b c
echo $foo[@]
# a[@]

Cette page est une bonne introduction aux tableaux dans Bash.

Citations revisitées ( ${foo[@]}vs. "${foo[@]}")

Vous n'avez pas posé de questions à ce sujet, mais c'est une différence subtile qu'il est bon de connaître. Si les éléments de votre tableau peuvent contenir des espaces, vous devez utiliser des guillemets doubles pour que chaque élément soit traité comme un «mot» distinct:

foo=("the first" "the second")
for i in "${foo[@]}"; do # Expands to 'for i in "the first" "the second"; do...'
    echo $i              #   so the loop runs twice
done
# the first
# the second

Comparez cela avec le comportement sans guillemets doubles:

foo=("the first" "the second")
for i in ${foo[@]}; do # Expands to 'for i in the first the second; do...'
    echo $i            #   so the loop runs four times!
done
# the
# first
# the
# second
CeSuitIsNoirNon
la source
3
Il existe un autre cas:, ${var:?}qui fournira une erreur lorsque la variable n'est pas définie ou n'est pas définie. REF: github.com/koalaman/shellcheck/wiki/SC2154
Nam Nguyen
4
@NamNguyen Si vous voulez parler d' autres formes d' expansion des paramètres , il y a au moins une douzaine d' autres: ${parameter:-word}, ${parameter:=word}, ${parameter#word}, ${parameter/pattern/string}et ainsi de suite. Je pense que cela dépasse le cadre de cette réponse.
ThisSuitIsBlackNot
En fait, la discussion sur les guillemets doubles est un peu incomplète. Voir plus stackoverflow.com/questions/10067266/…
tripleee
11

TL; DR

Tous les exemples que vous donnez sont des variantes des extensions Bash Shell . Les extensions se produisent dans un ordre particulier et certaines ont des cas d'utilisation spécifiques.

Accolades en tant que délimiteurs de jetons

La ${var}syntaxe est principalement utilisée pour délimiter les jetons ambigus. Par exemple, considérez ce qui suit:

$ var1=foo; var2=bar; var12=12
$ echo $var12
12
$ echo ${var1}2
foo2

Accolades dans les extensions de tableau

Les accolades sont nécessaires pour accéder aux éléments d'un tableau et pour d'autres extensions spéciales . Par exemple:

$ foo=(1 2 3)

# Returns first element only.
$ echo $foo
1

# Returns all array elements.
$ echo ${foo[*]}
1 2 3

# Returns number of elements in array.
$ echo ${#foo[*]}
3

Tokenisation

La plupart de vos autres questions concernent la citation et la manière dont le shell tokenise l'entrée. Considérez la différence dans la façon dont le shell effectue la division des mots dans les exemples suivants:

$ var1=foo; var2=bar; count_params () { echo $#; }

# Variables are interpolated into a single string.
$ count_params "$var1 $var2"
1

# Each variable is quoted separately, created two arguments.
$ count_params "$var1" "$var2"
2

Le @symbole interagit avec la citation différemment de *. Plus précisément:

  1. $@ "[e] xpands aux paramètres de position, en commençant par un. Lorsque le développement se produit entre guillemets, chaque paramètre se développe en un mot séparé."
  2. Dans un tableau, "[i] si le mot est entre guillemets, se ${name[*]}développe en un seul mot avec la valeur de chaque membre du tableau séparé par le premier caractère de la variable IFS, et ${name[@]}développe chaque élément du nom en un mot séparé."

Vous pouvez voir cela en action comme suit:

$ count_params () { echo $#; }
$ set -- foo bar baz 

$ count_params "$@"
3

$ count_params "$*"
1

L'utilisation d'un développement entre guillemets est très importante lorsque les variables font référence à des valeurs avec des espaces ou des caractères spéciaux qui pourraient empêcher le shell de diviser les mots comme vous le souhaitez. Voir Citations pour plus d'informations sur le fonctionnement des citations dans Bash.

Todd A. Jacobs
la source
7

Vous devez faire la distinction entre les tableaux et les variables simples - et votre exemple utilise un tableau.

Pour les variables simples:

  • $varet ${var}sont exactement équivalents.
  • "$var"et "${var}"sont exactement équivalents.

Cependant, les deux paires ne sont pas identiques à 100% dans tous les cas. Considérez la sortie ci-dessous:

$ var="  abc  def  "
$ printf "X%sX\n" $var
XabcX
XdefX
$ printf "X%sX\n" "${var}"
X  abc  def  X
$

Sans les guillemets doubles autour de la variable, l'espacement interne est perdu et le développement est traité comme deux arguments de la printfcommande. Avec les guillemets doubles autour de la variable, l'espacement interne est conservé et le développement est traité comme un argument de la printfcommande.

Avec les tableaux, les règles sont à la fois similaires et différentes.

  • Si groupsest un tableau, référençant $groupsou ${groups}équivaut à référencer ${groups[0]}, l'élément zéro du tableau.
  • Le référencement "${groups[@]}"est analogue au référencement "$@"; il préserve l'espacement entre les éléments individuels du tableau et renvoie une liste de valeurs, une valeur par élément du tableau.
  • Le référencement ${groups[@]}sans les guillemets doubles ne préserve pas l'espacement et peut introduire plus de valeurs qu'il n'y a d'éléments dans le tableau si certains des éléments contiennent des espaces.

Par exemple:

$ groups=("abc def" "  pqr  xyz  ")
$ printf "X%sX\n" ${groups[@]}
XabcX
XdefX
XpqrX
XxyzX
$ printf "X%sX\n" "${groups[@]}"
Xabc defX
X  pqr  xyz  X
$ printf "X%sX\n" $groups
XabcX
XdefX
$ printf "X%sX\n" "$groups"
Xabc defX
$

Utiliser *au lieu de @conduit à des résultats subtilement différents.

Voir aussi Comment parcourir les arguments d'un bashscript .

Jonathan Leffler
la source
3

La deuxième phrase du premier paragraphe sous extension paramètre en man bashdit,

Le nom du paramètre ou le symbole à développer peut être entouré d'accolades, qui sont facultatives mais servent à protéger la variable à développer des caractères qui la suivent immédiatement et qui pourraient être interprétés comme faisant partie du nom.

Ce qui vous indique que le nom est simplement des accolades , et que le but principal est de clarifier où le nom commence et se termine:

foo='bar'
echo "$foobar"
# nothing
echo "${foo}bar"
barbar

Si vous lisez plus loin vous découvrez,

Les accolades sont obligatoires lorsque le paramètre est un paramètre de position avec plus d'un chiffre…

Testons:

$ set -- {0..100}
$ echo $22
12
$ echo ${22}
20

Huh. Soigné. Honnêtement, je ne le savais pas avant d'écrire ceci (je n'avais jamais eu plus de 9 paramètres de position auparavant.)

Bien sûr, vous avez également besoin d'accolades pour faire les puissantes fonctionnalités d'extension de paramètres telles que

${parameter:-word}
${parameter:=word}
${parameter:?word}
 [read the section for more]

ainsi que l'extension de la baie.

Kojiro
la source
3

Un cas connexe non couvert ci-dessus. Citant une variable vide semble changer les choses pour test -n. Ceci est spécifiquement donné à titre d'exemple dans le infotexte pour coreutils, mais pas vraiment expliqué:

16.3.4 String tests
-------------------

These options test string characteristics.  You may need to quote
STRING arguments for the shell.  For example:

     test -n "$V"

  The quotes here prevent the wrong arguments from being passed to
`test' if `$V' is empty or contains special characters.

J'adorerais entendre l'explication détaillée. Mes tests le confirment, et je cite maintenant mes variables pour tous les tests de chaînes, pour éviter d'avoir -zet -nrenvoyer le même résultat.

$ unset a
$ if [ -z $a ]; then echo unset; else echo set; fi
unset
$ if [ -n $a ]; then echo set; else echo unset; fi    
set                                                   # highly unexpected!

$ unset a
$ if [ -z "$a" ]; then echo unset; else echo set; fi
unset
$ if [ -n "$a" ]; then echo set; else echo unset; fi
unset                                                 # much better
au nord
la source
2

Eh bien, je sais que l'encapsulation d'une variable vous aide à travailler avec quelque chose comme:

${groups%example}

ou une syntaxe comme celle-là, où vous voulez faire quelque chose avec votre variable avant de renvoyer la valeur.

Maintenant, si vous voyez votre code, toute la magie est à l'intérieur

${groups[@]}

la magie est là parce que vous ne pouvez pas écrire simplement: $groups[@]

Vous placez votre variable dans le {}parce que vous souhaitez utiliser des caractères spéciaux []et @. Vous ne pouvez pas nommer ou appeler votre variable simplement: @ou something[]parce que ce sont des caractères réservés pour d'autres opérations et noms.

Sierisimo
la source
Cela ne permet pas de souligner la signification très significative des guillemets doubles et comment le code sans eux est fondamentalement cassé.
tripleee