Quand avons-nous besoin d'accolades autour des variables shell?

659

Dans les scripts shell, quand utilisons-nous {}pour développer des variables?

Par exemple, j'ai vu ce qui suit:

var=10        # Declare variable

echo "${var}" # One use of the variable
echo "$var"   # Another use of the variable

Y a-t-il une différence significative, ou est-ce simplement du style? Est-ce que l'un est préféré à l'autre?

Nouvel utilisateur
la source

Réponses:

754

Dans cet exemple particulier, cela ne fait aucune différence. Cependant, l' {}en ${}sont utiles si vous souhaitez étendre la variable foodans la chaîne

"${foo}bar"

puisque "$foobar"développerait plutôt la variable identifiée par foobar.

Les accolades sont également nécessaires sans condition lorsque:

  • expansion des éléments du tableau, comme dans ${array[42]}
  • en utilisant des opérations d'expansion de paramètres, comme dans ${filename%.*}(supprimer l'extension)
  • extension des paramètres de position au-delà de 9: "$8 $9 ${10} ${11}"

Faire cela partout, au lieu de simplement dans des cas potentiellement ambigus, peut être considéré comme une bonne pratique de programmation. C'est à la fois pour la cohérence et pour éviter les surprises comme $foo_$bar.jpg, où il n'est pas évident visuellement que le trait de soulignement fasse partie du nom de la variable.

Fred Foo
la source
106
{}est connu comme l' expansion de l'orthèse . ${}est connu comme l'expansion variable. Ils font des choses différentes. Je vous voterais à part le bit sans expansion.
Spencer Rathbun
5
@NewUser " Donc, à part les tableaux, ce n'est pas vraiment nécessaire " Non, les accolades sont nécessaires pour PARAMETER EXPANSION , une construction très utile dans les scripts. J'ai vu de nombreux scripts sed et awk qui peuvent être remplacés par un peu d'extension des paramètres.
SiegeX
10
@caffinatedmonkey $()est utilisé pour exécuter une commande, telle qu'elle md5sum=$(md5sum foo.bin)stockera la sortie de md5sum foo.bindans la variable md5sum, désormais accessible via ${md5sum}. De plus, +1 et bien d'autres encore dans l'esprit d'OP pour avoir mentionné que c'est une bonne pratique d'être explicite!
L0j1k
11
@ L0j1k En parlant d'explicitness, je trouve important de mentionner qu'il $()exécute sa commande à partir d'un sous - shell .
Adrian Günter
2
@karatedog ${1:-20}est une forme d'expansion des paramètres. Ici, ce n'est pas évident car il utilise principalement des chiffres et des opérateurs arithmétiques qui nous trompent en pensant qu'il y a de l'arithmétique, mais il se réfère en fait au paramètre positionnel $1qui, s'il n'est pas défini, sera remplacé par une valeur par défaut 20(la syntaxe est ${variable:-default_value}).
Aaron
127

Les variables sont déclarées et affectées sans $et sans {}. Vous devez utiliser

var=10

assigner. Pour lire à partir de la variable (en d'autres termes, "développer" la variable), vous devez utiliser $.

$var      # use the variable
${var}    # same as above
${var}bar # expand var, and append "bar" too
$varbar   # same as ${varbar}, i.e expand a variable called varbar, if it exists.

Cela m'a parfois dérouté - dans d'autres langues, nous nous référons à la variable de la même manière, qu'elle se trouve à gauche ou à droite d'une affectation. Mais le shell-scripting est différent, $var=10ne fait pas ce que l'on pourrait penser!

Aaron McDaid
la source
34

Vous utilisez {}pour le regroupement. Les accolades sont nécessaires pour déréférencer les éléments du tableau. Exemple:

dir=(*)           # store the contents of the directory into an array
echo "${dir[0]}"  # get the first entry.
echo "$dir[0]"    # incorrect
glenn jackman
la source
Je ne pouvais pas comprendre la première ligne dir=(*). Autant que je sache, direst une commande intégrée pour lister le contenu du répertoire (équivalent à ls -C -b). Pourriez-vous s'il vous plaît expliquer?
Jarvis
1
Dans la programmation shell, les commandes et les arguments doivent être séparés les uns des autres par des espaces. Ici, vous voyez le signe égal sans espace, ce qui signifie qu'il s'agit d'une affectation de variable. direst le nom de la variable et les parenthèses sont utilisées pour collecter l'expansion du nom de fichier *dans un tableau.
glenn jackman
27

Vous pouvez également effectuer une manipulation de texte à l'intérieur des accolades:

STRING="./folder/subfolder/file.txt"
echo ${STRING} ${STRING%/*/*}

Résultat:

./folder/subfolder/file.txt ./folder

ou

STRING="This is a string"
echo ${STRING// /_}

Résultat:

This_is_a_string

Vous avez raison dans "les variables régulières" ne sont pas nécessaires ... Mais c'est plus utile pour le débogage et pour lire un script.

SierraX
la source
11

La fin du nom de la variable est généralement indiquée par un espace ou une nouvelle ligne. Mais que se passe-t-il si nous ne voulons pas d'espace ou de nouvelle ligne après avoir imprimé la valeur de la variable? Les accolades indiquent à l'interpréteur shell où se trouve la fin du nom de la variable.

Exemple classique 1) - variable shell sans espace de fin

TIME=10

# WRONG: no such variable called 'TIMEsecs'
echo "Time taken = $TIMEsecs"

# What we want is $TIME followed by "secs" with no whitespace between the two.
echo "Time taken = ${TIME}secs"

Exemple 2) Chemin de classe Java avec des fichiers JAR versionnés

# WRONG - no such variable LATESTVERSION_src
CLASSPATH=hibernate-$LATESTVERSION_src.zip:hibernate_$LATEST_VERSION.jar

# RIGHT
CLASSPATH=hibernate-${LATESTVERSION}_src.zip:hibernate_$LATEST_VERSION.jar

(La réponse de Fred le dit déjà mais son exemple est un peu trop abstrait)

Sridhar Sarnobat
la source
5

Les accolades sont toujours nécessaires pour accéder aux éléments du tableau et effectuer l'expansion des accolades.

Il est bon de ne pas être trop prudent et d'utiliser {}pour l'expansion des variables shell même lorsqu'il n'y a pas de place pour l'ambiguïté.

Par exemple:

dir=log
prog=foo
path=/var/${dir}/${prog}      # excessive use of {}, not needed since / can't be a part of a shell variable name
logfile=${path}/${prog}.log   # same as above, . can't be a part of a shell variable name
path_copy=${path}             # {} is totally unnecessary
archive=${logfile}_arch       # {} is needed since _ can be a part of shell variable name

Il est donc préférable d'écrire les trois lignes comme suit:

path=/var/$dir/$prog
logfile=$path/$prog.log
path_copy=$path

ce qui est certainement plus lisible.

Puisqu'un nom de variable ne peut pas commencer par un chiffre, le shell n'a pas besoin de {}variables numérotées (comme $1, $2etc.) à moins qu'une telle expansion ne soit suivie d'un chiffre. C'est trop subtil et cela rend l'utilisation explicite {}dans de tels contextes:

set app      # set $1 to app
fruit=$1le   # sets fruit to apple, but confusing
fruit=${1}le # sets fruit to apple, makes the intention clear

Voir:

codeforester
la source
1
It's good to be not over-cautious: Je me demande ce que la plupart des gens pensent. Utilisez des accolades tout le temps pour ne pas les oublier quand vous en avez besoin, ou utilisez-les uniquement là où vous en avez besoin, pour améliorer la lisibilité.
Roger Dahl
1
Je pense que c'est le manque de sensibilisation qui conduit les programmeurs à utiliser des curlies même lorsqu'ils ne sont pas nécessaires. Cette ignorance est similaire à l'autre erreur courante de ne pas utiliser de guillemets doubles pour empêcher le fractionnement ou la globalisation de mots par inadvertance. À la base de cela, la réalité est que les programmeurs ne sont pas sérieux au sujet des scripts shell autant que d'autres langages de script comme Python et Ruby.
codeforester
1
Vrai que. Ma bête noire est que tout le monde semble penser que toutes les variables doivent être en majuscules dans les scripts shell :)
Roger Dahl
2

Suite à la suggestion de SierraX et de Peter concernant la manipulation de texte, des accolades {}sont utilisées pour passer une variable à une commande, par exemple:

Disons que vous avez un fichier sposi.txt contenant la première ligne d'un roman italien bien connu:

> sposi="somewhere/myfolder/sposi.txt"
> cat $sposi

Sortie: quel ramo del lago di como che volge a mezzogiorno

Créez maintenant deux variables:

# Search the 2nd word found in the file that "sposi" variable points to
> word=$(cat $sposi | cut -d " " -f 2)

# This variable will replace the word
> new_word="filone"

Maintenant, remplacez le mot variable content par celui de new_word , dans le fichier sposi.txt

> sed -i "s/${word}/${new_word}/g" $sposi
> cat $sposi

Sortie: quel filone del lago di como che volge a mezzogiorno

Le mot "ramo" a été remplacé.

Fabio Natalini
la source
1
Cela fonctionne aussi bien sans accolades autour des variables.
Armali
Vous voudrez peut-être réparer le weel-known novelbit. Ont néanmoins voté.
gsl