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

que dit le titre: qu'est-ce que cela signifie d'encapsuler une variable dans {} , "" , ou "{} "? Je n'ai pas été en mesure de trouver des explications en ligne à ce sujet - je n'ai pas été en mesure de faire référence à eux, sauf pour l'utilisation des symboles, qui 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")

:

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

s'avère être très différente:

for group in $groups; do
    echo $group
done

et ceci:

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

seul le premier accomplit ce que je veux: itérer à travers 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 vous en serais reconnaissant.

comme question supplémentaire - est-ce que quelqu'un connaît la façon acceptée de faire référence à ces encapsulations?

72
demandé sur Shashank Agrawal 2013-08-09 00:25:19

6 réponses

bretelles ( $var vs. ${var} )

dans la plupart des cas, $var et ${var} sont les mêmes:

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

Les accolades ne sont nécessaires 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 ( $var vs. "$var" vs. "${var}" )

quand 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 blancs:

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 le suivant:

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 $var vs. ${var} , les supports sont seulement nécessaires pour la désambiguïsation, 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 aussi être écrit "${var}"bar , auquel cas vous n'avez plus besoin des bretelles, i.e. "$var"bar . Toutefois, si vous avez beaucoup de citations dans votre chaîne, ces formes alternatives peuvent devenir difficiles à lire (et donc difficiles à maintenir). Cette page fournit une bonne introduction à la cotation en Bash.

Arrays ( $var vs. $var[@] vs. ${var[@]} )

à vous. Selon le manuel de bash :

se référant à une variable de tableau sans un indice est équivalent à référencement du tableau avec un indice de 0.

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

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

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 @ que l'indice, par exemple ${foo[@]} . Les bracelets sont nécessaires avec les tableaux, car sans eux, la coque se dilaterait le $foo partie première, 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 de Bash.

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

vous n'avez pas demandé à ce sujet, mais c'est une différence subtile qui est bon à savoir. Si les éléments de votre tableau peut contenir des espaces, vous devez utiliser doublez les guillemets 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

contraste avec le comportement sans guillemets:

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
130
répondu ThisSuitIsBlackNot 2013-08-09 16:16:06

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

De simples variables:

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

cependant, les deux paires ne sont pas 100% identiques dans tous les cas. Considérons le résultat ci-dessous:

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

sans les doubles guillemets autour de la variable, l'espacement interne est perdu et l'expansion est traitée comme deux arguments à la commande printf . Avec les guillemets doubles autour de la variable, l'espacement interne est préservé et l'expansion est traitée comme un argument à la commande printf .

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

  • si groups est un tableau, faire référence à $groups ou ${groups} revient à faire référence à ${groups[0]} , l'élément zérot du tableau.
  • référencement "${groups[@]}" est analogue au référencement "$@" ; il préserve l'espacement dans les éléments individuels du tableau, et renvoie une liste de valeurs, une valeur par élément du tableau.
  • référencement ${groups[@]} sans les doubles guillemets ne préserve pas l'espacement et peut introduire plus de valeurs que il y a des éléments dans le tableau si certains des éléments qui 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
$

L'utilisation de * au lieu de @ conduit à des résultats subtilement différents.

Voir aussi Comment effectuer une itération sur les arguments dans un bash script .

6
répondu Jonathan Leffler 2017-05-23 12:02:59

TL; DR

tous les exemples que vous donnez sont des variations sur Bash Shell Expansions . Les Expansions se produisent dans un ordre particulier, et certains ont des cas d'utilisation spécifiques.

accolades comme délimiteurs de jeton

la syntaxe ${var} est principalement utilisée pour délimiter des jetons Ambigus. Par exemple, considérons ce qui suit:

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

bretelles dans L'expansion des réseaux

Les accolades sont obligatoires pour accéder aux éléments d'un array et pour les autres spécial expansions . 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

Tokenization

la plupart des autres questions ont trait à la citation et à la façon dont le shell tokenizes input. Considérez la différence dans la façon dont le shell exécute word splitting 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 une citation différente de * . Plus précisément:

  1. $@ "[e]xplique aux paramètres de position, à partir d'un. Lorsque l'expansion se produit dans des guillemets doubles, chaque paramètre se développe à un mot séparé."
  2. dans un tableau, "si le mot est double-Cité, ${name[*]} se développe à un seul mot avec la valeur de chaque membre du tableau séparé par le premier caractère de la variable IFS, et ${name[@]} étend chaque élément de nom à un mot séparé."

vous pouvez le voir en action comme suit:

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

$ count_params "$@"
3

$ count_params "$*"
1

l'utilisation d'une extension Citée est très importante lorsque des variables se réfèrent à des valeurs avec des espaces ou des caractères spéciaux qui pourraient empêcher le shell de partager des mots comme vous le souhaitez. Voir citant pour plus de détails sur comment citer fonctionne à Bash.

6
répondu Todd A. Jacobs 2013-08-08 21:53:23

la deuxième phrase du premier paragraphe sous expansion du paramètre dans man bash dit,

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

qui vous dit que le nom est simplement bracelets , et 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 entretoises sont nécessaires lorsque le paramètre est un paramètre de position à plus d'un chiffre...

test:

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

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

bien sûr, vous avez aussi besoin de bretelles pour faire les puissantes fonctionnalités d'extension de paramètre comme

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

ainsi que l'expansion du réseau.

3
répondu kojiro 2013-08-08 20:47:12

Un cas lié à la non couverts ci-dessus. Citant une variable vide semble changer les choses pour test -n . Ceci est spécifiquement donné comme exemple dans le texte info 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'aimerais entendre l'explication détaillée. Mes tests le confirment, et je cite maintenant mes variables pour tous les tests de chaîne, pour éviter d'avoir -z et -n retourner 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
3
répondu nortally 2013-11-19 17:51:23

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

${groups%example}

ou une syntaxe comme ça, où vous voulez faire quelque chose avec votre variable avant de retourner 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 juste: $groups[@]

vous mettez votre variable à l'intérieur de la {} parce que vous voulez utiliser les caractères spéciaux [] et @ . Vous ne pouvez pas nommer ou appeler votre variable juste: @ ou something[] parce que ce sont des caractères réservés pour d'autres opérations et noms.

2
répondu Sierisimo 2016-02-15 09:58:09