Est-il possible d'imprimer le contenu du contenu d'une variable avec un script shell? (référencement indirect)

13

Supposons que j'ai déclaré les variables suivantes:

$ var='$test'
$ test="my string"

Si j'imprime leur contenu, je vois ce qui suit:

$ echo $var
$test

$ echo $test
my string

Je voudrais trouver un moyen d'imprimer le contenu du contenu de $var(qui est le contenu de $test). J'ai donc essayé de faire ce qui suit:

$ echo $(echo $var)
$test

Mais ici le résultat est $testet non "my string"... Est-il possible d'imprimer le contenu du contenu des variables en utilisant bash?

Rafael Muynarsk
la source
Pourriez-vous modifier votre message pour indiquer le shell que vous utilisez ou les exigences de portabilité que vous avez?
Michael Homer
@MichaelHomer Bien sûr, mais comment puis-je vérifier exactement ces informations?
Rafael Muynarsk
C'est plus quelque chose que vous choisissez que quelque chose à vérifier. Par exemple, exécutez-vous vos scripts avec ash, ou bash, ou csh, ou dash, ..., ou zsh, ou POSIX sh, ou avez-vous besoin de travailler sur différents systèmes?
Michael Homer
@MichaelHomer Ah, je vois ... J'utilise bash. Je vais juste changer la dernière question et insérer ensuite une nouvelle balise.
Rafael Muynarsk

Réponses:

21

Vous pouvez accomplir cela en utilisant l'expansion de variable indirecte de bash (tant que vous pouvez laisser de côté $votre variable de référence):

$ var=test
$ test="my string"
$ echo "$var"
test
$ echo "${!var}"
my string

3.5.3 Extension des paramètres du shell

jesse_b
la source
10

Dans le cas où le nom de variable contenu dans varest préfixé, $vous pouvez utiliser eval:

$ var='$test'
$ test="my string"
$ eval echo $var
my string

Que se passe t-il ici:

  • bash se développe $varà la valeur $test, produisant une eval echo $testcommande;
  • evalévalue l' echo $testexpression et produit un résultat souhaité.

Notez que l'utilisation evalen général peut être dangereuse (selon ce qui est stocké var), alors préférez l'éviter. La fonctionnalité d'extension indirecte est meilleure pour votre cas (mais vous devez vous débarrasser de la $connexion $test).

Danila Kiver
la source
Bien sûr, c'est une erreur de frappe. Le message d'origine a des guillemets simples. Fixé.
Danila Kiver
4
Je recommande toujours les références de variable entre guillemets doubles (pour éviter les problèmes dus au fractionnement de mots inattendu et à l'expansion des caractères génériques). Avec eval, ligne, vous avez besoin de deux couches de guillemets doubles: eval echo "\"$var\"". Attention, cela ne fait rien sur les autres dangers d'utilisation evalque vous avez mentionnés.
Gordon Davisson
9

Similaire à la réponse de Jesse_b , mais en utilisant une variable de référence de nom au lieu d'une indirection variable (nécessite bash4.3+):

$ declare -n var=test
$ test="my string"
$ echo "$var"
my string

La variable de référence de nom varcontient le nom de la variable à laquelle elle fait référence. Lorsque la variable est déréférencée en tant que $var, la valeur de cette autre variable est renvoyée.

bash résout les références de nom de manière récursive:

$ declare -n var1=var2
$ declare -n var2=test
$ test="hello world"
$ echo "$var1"
hello world

Pour être complet, l'utilisation d'un tableau associatif (dans la version bash4.0+) est également un moyen de résoudre ce problème, en fonction des exigences:

$ declare -A strings
$ strings[test]="my string"
$ var=test
$ echo "${strings[$var]}"
my string

Cela fournit un moyen plus flexible d'accéder à plus d'une valeur par une clé ou un nom qui peut être déterminé dynamiquement. Cela peut être préférable si vous souhaitez collecter toutes les valeurs d'une catégorie particulière dans un seul tableau, tout en étant en mesure d'y accéder par une clé (par exemple, les noms accessibles par ID ou les noms de chemin accessibles par objectif, etc.) car cela ne pollue pas l'espace de noms variable du script.

Kusalananda
la source
3

Avec zsh:

$ var='$test'
$ test='*'
$ printf '%s\n' ${(e)var}
*

Avec n'importe quelle coque de type Bourne

$ eval 'printf "%s\n" "'"$var"'"'
*

Rappelez-vous que dans ces shells, les extensions de variables doivent être entre guillemets pour empêcher split + glob ( zshétant une exception) et echone peuvent pas être utilisées pour des données arbitraires.

Comme avec les deux (e)et evalqu'il y a une évaluation de code shell, il est important que le contenu $varreste sous votre contrôle, car sinon cela peut être une vulnérabilité d'injection de commande arbitraire (même avec les ${!var}approches).

Stéphane Chazelas
la source