Retour indirect de tous les éléments d'un tableau

10

La page de manuel Bash décrit l'utilisation de ${!a}pour renvoyer le contenu de la variable dont le nom est le contenu de a(un niveau d'indirection).

Je voudrais savoir comment retourner tous les éléments d'un tableau à l'aide de ceci, c'est-à-dire

a=(one two three)
echo ${a[*]}

Retour

one two three

Je voudrais pour:

b=a
echo ${!b[*]}

pour retourner le même. Malheureusement, ce n'est pas le cas, mais revient à la 0place.

Mise à jour

Compte tenu des réponses, je me rends compte maintenant que mon exemple était trop simple, car bien sûr, quelque chose comme:

b=("${a[@]}")

Réalisera exactement ce dont j'avais besoin.

Alors, voici ce que j'essayais de faire:

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST=LIST_$whichone
LIST=${!_LIST[*]}

Bien sûr, une lecture attentive de la page de manuel de Bash montre que cela ne fonctionnera pas comme prévu car la dernière ligne renvoie simplement les indices du "tableau" $_LIST(pas du tout un tableau).

Dans tous les cas, ce qui suit devrait faire le travail (comme indiqué):

LIST=($(eval echo \${$_LIST[*]}))

ou ... (l'itinéraire que j'ai suivi, finalement):

LIST_lys="lys1 lys2"
...
LIST=(${!_LIST})

En supposant, bien sûr, que les éléments ne contiennent pas d'espace.

Eric Smith
la source
Ajoutez [@]au pointeur _LIST="LIST_${whichone}[@]", puis utilisez LIST=("${!_LIST}")pour copier le tableau. C'est une bonne idée d'utiliser des noms de variable en minuscule pour éviter les conflits avec les variables d'environnement (toutes majuscules).
Isaac

Réponses:

7

Je pense que l'utilisation de la référence indirecte de la variable bash doit être traitée littéralement.

Par exemple. Pour votre exemple original:

a=(one two three)
echo ${a[*]} # one two three
b=a
echo ${!b[*]} # this would not work, because this notation 
              # gives the indices of the variable b which
              # is a string in this case and could be thought
              # as a array that conatins only one element, so
              # we get 0 which means the first element
c='a[*]'
echo ${!c} # this will do exactly what you want in the first
           # place

Pour le dernier vrai scénario, je crois que le code ci-dessous ferait le travail.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[*]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one

Il est préférable d'utiliser une notation "${var[@]}"qui évite de perturber l' $IFSexpansion des paramètres et. Voici le code final.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[@]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one
                     # It is essential to have ${!_LIST} quoted
weynhamz
la source
8

Vous devez copier les éléments explicitement. Pour un tableau indexé:

b=("${a[@]}")

Pour un tableau associatif (notez que ac'est le nom de la variable du tableau, pas une variable dont la valeur est le nom d'une variable du tableau):

typeset -A b
for k in "${!a[@]}"; do b[$k]=${a[$k]}; done

Si vous avez le nom de variable dans un tableau, vous pouvez utiliser la méthode élément par élément avec une étape supplémentaire pour récupérer les clés.

eval "keys=(\"\${!$name[@]}\")"
for k in "${keys[@]}"; do eval "b[\$k]=\${$name[\$k]}"; done

(Attention, le code de cet article a été tapé directement dans un navigateur et n'a pas été testé.)

Gilles 'SO- arrête d'être méchant'
la source
Vous avez tout à fait raison, mais malheureusement, cela ne résout pas mon problème (mon exemple était trop simpliste). J'ai fourni une mise à jour pour être plus clair sur mon intention.
Eric Smith
@Eric Je pense qu'en ksh / bash, vous avez besoin d'une évaluation à ce stade. Voir mon montage.
Gilles 'SO- arrête d'être méchant'
+1, mais selon votre avis de non-responsabilité, cela ne fonctionne pas vraiment (le dernier morceau de code), mais ne nécessite que quelques ajustements mineurs. Plus précisément, il doit se trouver \${!$name[@]}sur la première ligne, de sorte que la première extension ne soit que de «$ name» et que le ${!a[@]}soit enregistré pour l'eval, et la même chose dans la boucle for, avec \${$name}. Les deux autres barres obliques inverses avant «$ k» n'y sont pas strictement nécessaires non plus.
krb686
2

${!b[*]}se développe aux indices utilisés dans le tableau b.

Ce que vous voulez doit être fait en deux étapes, donc evalvous aidera: eval echo \${$b[*]}. (Notez le \qui garantit que le premier $passera la première étape, l'expansion variable, et ne sera développé qu'à la deuxième étape eval.)

Selon le paramètre Expansion !est utilisé à la fois pour l'expansion indirecte ( {!a}), les noms correspondant au préfixe ( ${!a*}) et la liste des clés du tableau ( ${!a[*]}). Étant donné que la liste des clés de tableau a la même syntaxe que votre expansion indirecte + expansion d'élément de tableau, celle-ci n'est pas prise en charge telle quelle.

homme au travail
la source
2
${!a}étend à la valeur de la variable dont le nom est $a. Ceci est assez sommairement décrit dans le manuel, dans le paragraphe qui commence par "Si le premier caractère du paramètre est un point d'exclamation ( !), un niveau d'indirection variable est introduit."
Gilles 'SO- arrête d'être méchant'
Oui - @Gilles a raison, mais @manatwork, en deuxième lecture, j'ai remarqué que ${!c'est un peu ambigu car si c'est un tableau avec lequel vous avez affaire, le comportement est différent.
Eric Smith
@Gilles, vous avez raison sur cette phrase, mais malheureusement cela ne s'applique pas car "Les exceptions à cela sont les extensions de $ {! Prefix *} et $ {! Name [@]} décrites ci-dessous." Mais ma réponse est certainement un gâchis ambigu, donc je vais la modifier.
manatwork
0

Pour accéder indirectement aux tableaux, il suffit d'ajouter [@]à la variable indirecte b=a[@].

Si ces variables sont définies:

a=(one two three)
printf '<%s> ' "${a[@]}"; echo

Ensuite, cela fonctionnera:

b="a[@]"
printf '<%s> ' "${!b}"

Ou simplement:

echo "${!b}"

Un tel tableau pourrait être copié comme ceci:

newArr=( "${!b}" )

Et puis imprimé avec:

declare -p newArr

Dans un script:

#!/bin/bash
a=(one two three)
echo "original array"
printf '<%s> ' "${a[@]}"; echo

echo "Indirect array with variable b=a[@]"
b="a[@]"
printf '<%s> ' "${!b}"; echo

echo "New array copied in newArr, printed with declare"
newArr=( "${!b}" )
declare -p newArr

Bien sûr, tout ce qui précède copiera un tableau non clairsemé. Un dans lequel tous les index ont une valeur.

tableaux clairsemés

Un tableau clairsemé est un tableau qui peut avoir des éléments non définis.
Par exemple, a[8]=1234définit un élément et, en bash, 0 à 7 n'existent pas.

Pour copier un tel tableau clairsemé, utilisez cette méthode

  1. Imprimez l'ancien tableau:

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
    
  2. Remplacez le nom du tableau et capturez la chaîne:

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
  3. Évaluez la chaîne ainsi créée, le nouveau tableau a été défini:

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")
    
Isaac
la source