Utilisez une référence de variable «à l'intérieur» d'une autre variable

27

Je suis sûr que c'est relativement simple, je ne sais pas comment faire.

#!/usr/bin/ksh
set `iostat`
myvar=6

Je veux quelque chose comme echo ${$myvar}ce que je veux interprété comme ${$myvar}-> ${6}->value

Brandon Kreisel
la source
4
Le terme technique est indirection variable .
Thor

Réponses:

29

Vous pouvez le faire avec eval, intégré à de nombreux shells fins, y compris ksh:

#!/usr/bin/ksh
set $(iostat)
myvar=6
eval "echo \${$myvar}"

L'astuce consiste à mettre entre guillemets la chaîne dans laquelle vous alimentez evalafin que $ myvar soit remplacé par "6", et à inverser le signe dollar extérieur, pour evalobtenir une chaîne "$ 6".

J'ai obtenu "% user" pour la sortie, mais je l'ai essayé sur une machine RHEL multiprocesseur.

Bruce Ediger
la source
4
Vous êtes officiellement le Grand Maître Suprême Exalté de la semaine b / c qui travaille même sur l'insondable horrible ksh (vraiment pdksh) dans OpenBSD 5.4. Si vous souhaitez définir var vv sur la valeur de la var dont le nom est dans la var vn , faites-le vv=$( eval "echo \$$vn" ). Merci beaucoup!
execNext
25

Référence de variable indirecte

Les shells avancés modernes ont une méthode pour référencer la valeur d'une variable dont le nom est stocké dans une autre variable. Malheureusement, la méthode diffère entre ksh, bash et zsh.

Dans mksh ≥R39b, vous pouvez faire myvarun nom:

typeset -n myvar=6
echo "$myvar"

Cela ne fonctionne pas dans ATT ksh93 car il ne prend pas en charge les noms de référence des paramètres positionnels. Dans le cas où vous avez une variable contenant un nom de variable, vous pouvez utiliser cette méthode.

foo=bar
typeset -n myvar=foo
echo "$myvar"  # prints bar

En bash ≥2.0, vous pouvez écrire

echo "${!myvar}"

En zsh, vous pouvez écrire

echo ${(P)myvar}

Dans les shells plus anciens, y compris ksh88 et pdksh, votre seul recours lorsque vous avez une variable contenant un autre nom de variable et que vous souhaitez utiliser la valeur de cette variable eval, comme expliqué par Bruce Ediger . Cette solution fonctionne dans n'importe quel shell Bourne / POSIX.

eval "value=\${$myvar}"
echo "$value"

Utilisation d'un tableau

C'est la meilleure méthode ici: c'est plus simple et plus portable.

Pour votre cas d'utilisation, dans n'importe quel shell avec des tableaux (toutes les variantes de ksh, bash ≥2.0, zsh), vous pouvez affecter à une variable de tableau et prendre l'élément que vous souhaitez. Attention, les tableaux ksh et bash commencent à numéroter à 0, mais zsh commence à 1 sauf si vous émettez setopt ksh_arraysou emulate ksh.

set -A iostat -- $(iostat)
echo "${iostat[5]}"

Si vous souhaitez copier les paramètres positionnels dans une variable de tableau a:

set -A a -- "$@"

Dans ksh93, mksh ≥R39b, bash ≥2.0 et zsh, vous pouvez utiliser la syntaxe d'affectation de tableau:

iostat=($(iostat))
echo "${iostat[5]}"
Gilles 'SO- arrête d'être méchant'
la source
Wow, votre solution 'Bourne / POSIX' fonctionne également dans ksh / pdksh d'OpenBSD 5.4. Pour l'appliquer à l'exemple de mon commentaire à la réponse de Bruce Ediger ci-dessus, faites-le eval "vv=\${$vn}". Merci beaucoup, gentil monsieur.
execNext
1

Comme indiqué par Gilles (qui a fourni la bashpartie de la réponse), n'invalidant pas non plus Bruce Ediger (sur la façon de le faire de manière portable avec eval), voici comment le faire namerefrécemment mksh(et AT&T ksh93, sauf - comme @Gilles l'a commenté - namerefs ne peut pas faire référence aux paramètres de position dans AT&T ksh, uniquement aux paramètres nommés):

#!/bin/mksh
set -- $(iostat)
nameref myvar=6
echo $myvar

Ajout de l' --after setpour une meilleure résistance également.

mirabilos
la source
Depuis ksh 93u, namerefs ne peut pas référencer les paramètres positionnels ( typeset: 6: invalid variable name).
Gilles 'SO- arrête d'être méchant'
0

Une autre utilisation des tableaux

Je n'ai utilisé ni ksh ni aucune variante depuis un certain temps, donc je ne sais pas si ksh (ou bash) a une capacité similaire. Mon shell principal est zsh. J'utilise des tableaux pour gérer la sortie de commandes comme iostat car elles produisent plusieurs lignes, et toutes les lignes n'ont pas le même format / la même longueur.

#! /bin/zsh
IOStatOutput=("${(@f)$(iostat)}") # Produces one element per line

Ce qui précède contourne également l'utilisation des paramètres de position. Maintenant, si vous voulez générer, disons, un ensemble de périphériques:

for Element in {7..${#IOStatOutput}} # Devices listed in elements 7 thru the last
do
    DevList+=( ${${=IOStatOutput[Element]}[1]} )
done

Je trouve les morceaux plus petits beaucoup plus faciles à manipuler. Vous pouvez ou non avoir besoin d'utiliser une référence de variable indirecte, selon votre code. Savoir comment cela fonctionne est toujours une bonne chose à savoir. Je l'utilise moi-même.

Friartek
la source