web-dev-qa-db-fra.com

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

Que dit le titre: que signifie encapsuler une variable dans {}, "", ou "{} "? Je n’ai pas pu trouver d’explications en ligne à ce sujet - je n’ai pas été en mesure de les consulter, si ce n’est pour l’utilisation des symboles, ce 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")

Cette:

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

Se révèle être très différent de cela:

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.

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

111
SheerSt

Bretelles ($var contre. ${var})

Dans la plupart des cas, $var et ${var} sont identiques:

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

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

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

Citations ($var contre. "$var" contre. "${var}")

Lorsque vous ajoutez des guillemets autour d'une variable, vous indiquez au shell de le traiter comme un seul mot, même s'il 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 $var contre. ${var}, les accolades ne sont nécessaires que 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 également être écrit "${var}"bar, auquel cas vous n’avez plus besoin des accolades, c.-à-d. "$var"bar. Cependant, si vous avez beaucoup de guillemets dans votre chaîne, ces formes alternatives peuvent être difficiles à lire (et donc difficiles à maintenir). Cette page fournit une bonne introduction à la citation dans Bash.

Tableaux ($var contre. $var[@] contre. ${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

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 nécessaires avec les tableaux car sans eux, le shell développerait le $foo première partie, 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[@]} contre. "${foo[@]}")

Vous n'avez pas posé de question à 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 "Word:" séparé.

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

Contrastez ceci 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
196
ThisSuitIsBlackNot

TL; DR

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

Accolades comme délimiteurs de jetons

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

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

Bretelles dans les extensions de tableau

Les accolades sont nécessaires pour accéder aux éléments d'un tablea et pour les 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

Tokenization

La plupart de vos autres questions portent sur les citations et sur la manière dont Shell transpose les entrées. Considérez la différence de performances du shell division de Word 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] étend les paramètres de position, en commençant par un. Lorsque le développement se produit entre guillemets doubles, chaque paramètre se développe en un mot distinct."
  2. Dans un tableau, "[i] si le mot est cité entre guillemets, ${name[*]} se 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[@]} étend chaque élément du nom en un mot séparé. "

Vous pouvez voir ceci 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 les variables font référence à des valeurs avec des espaces ou des caractères spéciaux susceptibles d'empêcher le shell de diviser Word de la manière que vous souhaitez. Voir Citations pour plus d'informations sur le fonctionnement des citations dans Bash.

11
Todd A. Jacobs

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

Pour les variables simples:

  • $var et ${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 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 guillemets autour de la variable, l'espacement interne est perdu et le développement est traité comme deux arguments de la commande printf. Avec les guillemets autour de la variable, l'espacement interne est préservé et le développement est traité comme un argument de la commande printf.

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

  • Si groups est un tableau, référençant $groups ou ${groups} revient à référencer ${groups[0]}, l'élément zeroth 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 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 é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
$

En utilisant * au lieu de @ conduit à des résultats légèrement différents.

Voir aussi comment parcourir les arguments dans un script bash .

7
Jonathan Leffler

La deuxième phrase du premier paragraphe sous Expansion des paramètres dans man bash dit,

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

Ce qui vous dit que le nom est simplement accolades, et le but principal est de clarifier où le nom commence et finit:

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

Si vous lisez plus loin, vous découvrez,

Les accolades sont obligatoires lorsque paramètre est un paramètre de position avec plusieurs chiffres…

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'ai 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'expansion des paramètres comme

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

ainsi que l'expansion du tableau.

3
kojiro

Un cas connexe non couvert ci-dessus. Citer une variable vide semble changer les choses pour test -n. Ceci est spécifiquement donné à titre d'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înes afin d'éviter d'avoir -z et -n retourne 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
nortally

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

${groups%example}

ou une syntaxe comme celle-ci, 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 mettez votre variable à l'intérieur du {} parce que vous voulez utiliser des caractères spéciaux [] et @. Vous ne pouvez pas nommer ou appeler votre variable simplement: @ ou something[] car il s'agit de caractères réservés pour d'autres opérations et noms.

2
Sierisimo