Comment utiliser une variable dans le cadre d'un nom de tableau


11

J'ai deux tableaux:

arrayA=(1 2 3)
arrayB=(a b c)

et je veux imprimer l'un d'eux en utilisant un argument de ligne de commande, c'est-à-dire sans aucun if else.

J'ai essayé quelques variantes de la syntaxe sans succès. Je veux faire quelque chose comme ça:

ARG="$1"

echo ${array${ARG}[@]}

mais j'obtiens une erreur de "mauvaise substitution". Comment puis-je atteindre cet objectif?


Ce n'est absolument pas idiot. S'il vous plaît, ne faites pas ça.
Wildcard

Réponses:


22

Essayez de faire ceci:

$ arrayA=(1 2 3)
$ x=A
$ var=array$x[@]
$ echo ${!var}
1 2 3

REMARQUE

  • à partir de man bash(expansion des paramètres):
    ${parameter}
           The value of parameter is substituted.
 The braces are required when parameter is a positional parameter with
  more than one

chiffre, ou lorsque le paramètre est suivi d'un caractère qui ne doit pas être interprété comme faisant partie de son nom.
* Si le premier caractère du paramètre est un point d'exclamation (!), Un niveau d'indirection variable est introduit. Bash utilise la valeur de la variable formée à partir du reste du paramètre comme nom de la variable; cette variable est ensuite développée et cette valeur est utilisée dans le reste de la substitution, plutôt que la valeur du paramètre lui-même. C'est ce qu'on appelle l'expansion indirecte. * Les exceptions à cette règle sont les extensions de $ {! Prefix *} et $ {! Name [@]} décrites ci-dessous. Le point d'exclamation doit suivre immédiatement l'accolade gauche pour introduire l'indirection.


Que fait exactement !devant la varvariable? Comment cela fonctionne-t-il, cela semblait être une substitution d'histoire sur Google, mais je ne pouvais pas voir comment cela fonctionnait ici.
Aaron

Voir mon article édité
Gilles Quenot

4

Bien que vous puissiez utiliser l'accès indirect comme indiqué dans une autre réponse , une autre façon (dans ksh et Bash 4.3 et plus récent) serait d'utiliser namerefs. Surtout dans le cas des tableaux, cela peut être plus utile car vous pouvez indexer le tableau via le nameref et n'avez pas besoin de mettre l'index dans la variable utilisée comme référence.

arr1=(a b c)
arr2=(x y z)
typeset -n p=arr1    # or 'declare -n' 
echo "${p[1]}"       # prints 'b'

Cela ne fonctionne pas via l'accès indirect:

q=arr2
echo "${!q}"         # prints 'x', the same as $arr2
echo "${!q[1]}"      # doesn't work, it tries to take q[1] as a reference

Comme un programmeur C pourrait le dire, ${!q[1]}agit ici comme s'il qs'agissait d'un tableau de pointeurs, au lieu d'être un pointeur vers un tableau.



1
arrayA=(1 2 3)
arrayB=(a b c)

ARG="$1"

eval echo \${array${ARG}[@]}

dataget (){ 
    eval echo \${array${1}[${2:-@}]}
}
$ dataget A
1 2 3
$ dataget A 0
1
$ dataget B 1
b

note: échapper aux cotes en cas d'espace !

eval dostuff \"\${array${1}[${2:-@}]}\"

1

Cela a pris beaucoup d'essais et d'erreurs, mais a finalement fonctionné.

Je me suis inspiré de Youness. Mais toutes les autres réponses n'ont pas aidé sur mon ancien bash (suse11sp1 [3.2.51 (1) -release])

La boucle «pour» a refusé d'étendre le tableau indirect, au lieu de cela, vous devez le pré-développer, utilisez-le pour créer un autre tableau avec votre nouveau nom de variable. Mon exemple ci-dessous montre une double boucle, car c'est mon utilisation prévue.

THEBIGLOOP=(New_FOO New_BAR)

FOOthings=(1 2 3)
BARthings=(a b c)

for j in ${THEBIGLOOP[*]}
do
    TheNewVariable=$(eval echo \${${j#New_}things[@]})

    for i in $TheNewVariable
        do
            echo  $j $i" hello"
        echo
    done
done

J'utilise # pour supprimer le "New_" de la première entrée du tableau, puis je concatène avec "things", pour obtenir "FOOthings". \ $ {} avec echo et eval, puis faites leur travail dans l'ordre sans lancer d'erreurs, qui est enveloppé dans un nouveau $ () et attribué le nouveau nom de variable.

$ Test.sh

New_FOO 1 hello

New_FOO 2 hello

New_FOO 3 hello

New_BAR a hello

New_BAR b hello

New_BAR c hello
MISE À JOUR ##### 2018/06/07

J'ai récemment découvert une autre rotation sur ce problème. La variable créée n'est pas en fait un tableau, mais une chaîne délimitée par des espaces. Pour la tâche ci-dessus, cela était correct, en raison de la façon dont "pour" fonctionne, il ne lit pas le tableau, il est développé puis bouclé, voir l'extrait ci-dessous:

for VARIABLE in 1 2 3 4 5 .. N
do
    command1
    command2
    commandN
done

Mais, j'ai ensuite dû l'utiliser comme un tableau. Pour cela, je devais effectuer une étape de plus. J'ai pris le code mot pour mot par Dennis Williamson . Je l'ai testé et ça marche bien.

IFS=', ' read -r -a TheNewVariable <<< ${TheNewVariable[@]}

"IFS = ','" est une variable contenant votre suppresseur. "read" avec "-a" coupe et réinjecte la piqûre dans la variable tableau. Remarque, cela n'a aucun respect pour les guillemets, mais il y a quelques options en lecture pour gérer cela, par exemple j'ai supprimé l'indicateur -r dont je n'avais pas besoin. J'ai donc combiné cet ajout dans la création de variable, ce qui permet de traiter et d'adresser les données comme il se doit.

THEBIGLOOP=(New_FOO New_BAR)

FOOthings=(1 2 3)
BARthings=(a b c)

for j in ${THEBIGLOOP[*]}
do

    IFS=', ' read -a TheNewVariable <<< $(eval echo \${${j#New_}things[@]})

    for i in ${TheNewVariable[@]}  #Now have to wrap with {} and expand with @
        do
            echo  $j $i" hello"
            echo  ${TheNewVariable[$i]}  #This would not work in the original code
        echo
    done
done

0

C'est ainsi que vous créeriez une variable nommée dynamiquement (version bash <4.3).

# Dynamically named array
my_variable_name="dyn_arr_names"
eval $my_variable_name=\(\)

# Adding by index to the array eg. dyn_arr_names[0]="bob"
eval $my_variable_name[0]="bob"

# Adding by pushing onto the array eg. dyn_arr_names+=(robert)
eval $my_variable_name+=\(robert\)

# Print value stored at index indirect
echo ${!my_variable_name[0]}

# Print value stored at index
eval echo \${$my_variable_name[0]}

# Get item count
eval echo \${#$my_variable_name[@]}

Vous trouverez ci-dessous un groupe de fonctions qui peuvent être utilisées pour gérer des tableaux nommés dynamiquement (version bash <4.3).

# Dynamically create an array by name
function arr() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
     # The following line can be replaced with 'declare -ag $1=\(\)'
     # Note: For some reason when using 'declare -ag $1' without the parentheses will make 'declare -p' fail
    eval $1=\(\)
}

# Insert incrementing by incrementing index eg. array+=(data)
function arr_insert() { 
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    eval $1[\$\(\(\${#${1}[@]}\)\)]=\$2
}

# Update an index by position
function arr_set() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    eval ${1}[${2}]=\${3}
}

# Get the array content ${array[@]}
function arr_get() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    eval echo \${${1}[@]}
}

# Get the value stored at a specific index eg. ${array[0]}  
function arr_at() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    [[ ! "$2" =~ ^(0|[-]?[1-9]+[0-9]*)$ ]] && { echo "Array index must be a number" 1>&2 ; return 1 ; }
    local v=$1
    local i=$2
    local max=$(eval echo \${\#${1}[@]})
    # Array has items and index is in range
    if [[ $max -gt 0 && $i -ge 0 && $i -lt $max ]]
    then 
        eval echo \${$v[$i]}
    fi
}

# Get the value stored at a specific index eg. ${array[0]}  
function arr_count() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable " 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    local v=${1}
    eval echo \${\#${1}[@]}
}



array_names=(bob jane dick)

for name in "${array_names[@]}"
do
    arr dyn_$name
done

echo "Arrays Created"
declare -a | grep "a dyn_"

# Insert three items per array
for name in "${array_names[@]}"
do
    echo "Inserting dyn_$name abc"
    arr_insert dyn_$name "abc"
    echo "Inserting dyn_$name def"
    arr_insert dyn_$name "def"
    echo "Inserting dyn_$name ghi"
    arr_insert dyn_$name "ghi"
done

for name in "${array_names[@]}"
do
    echo "Setting dyn_$name[0]=first"
    arr_set dyn_$name 0 "first"
    echo "Setting dyn_$name[2]=third"
    arr_set dyn_$name 2 "third"
done 

declare -a | grep "a dyn_"

for name in "${array_names[@]}"
do
    arr_get dyn_$name
done


for name in "${array_names[@]}"
do
    echo "Dumping dyn_$name by index"
    # Print by index
    for (( i=0 ; i < $(arr_count dyn_$name) ; i++ ))
    do
        echo "dyn_$name[$i]: $(arr_at dyn_$name $i)"

    done
done

for name in "${array_names[@]}"
do
    echo "Dumping dyn_$name"
    for n in $(arr_get dyn_$name)
    do
        echo $n
    done
done

Vous trouverez ci-dessous un groupe de fonctions qui peuvent être utilisées pour gérer des tableaux nommés dynamiquement (version bash> = 4.3).

# Dynamically create an array by name
function arr() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -g -a $1=\(\)   
}

# Insert incrementing by incrementing index eg. array+=(data)
function arr_insert() { 
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    declare -n r=$1
    r[${#r[@]}]=$2
}

# Update an index by position
function arr_set() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    declare -n r=$1 
    r[$2]=$3
}

# Get the array content ${array[@]}
function arr_get() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    declare -n r=$1 
    echo ${r[@]}
}

# Get the value stored at a specific index eg. ${array[0]}  
function arr_at() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    [[ ! "$2" =~ ^(0|[-]?[1-9]+[0-9]*)$ ]] && { echo "Array index must be a number" 1>&2 ; return 1 ; }
    declare -n r=$1 
    local max=${#r[@]}
    # Array has items and index is in range
    if [[ $max -gt 0 && $i -ge 0 && $i -lt $max ]]
    then 
        echo ${r[$2]}
    fi
}

# Get the value stored at a specific index eg. ${array[0]}  
function arr_count() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable " 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    declare -n r=$1
    echo ${#r[@]}
}



array_names=(bob jane dick)

for name in "${array_names[@]}"
do
    arr dyn_$name
done

echo "Arrays Created"
declare -a | grep "a dyn_"

# Insert three items per array
for name in "${array_names[@]}"
do
    echo "Inserting dyn_$name abc"
    arr_insert dyn_$name "abc"
    echo "Inserting dyn_$name def"
    arr_insert dyn_$name "def"
    echo "Inserting dyn_$name ghi"
    arr_insert dyn_$name "ghi"
done

for name in "${array_names[@]}"
do
    echo "Setting dyn_$name[0]=first"
    arr_set dyn_$name 0 "first"
    echo "Setting dyn_$name[2]=third"
    arr_set dyn_$name 2 "third"
done 

declare -a | grep 'a dyn_'

for name in "${array_names[@]}"
do
    arr_get dyn_$name
done


for name in "${array_names[@]}"
do
    echo "Dumping dyn_$name by index"
    # Print by index
    for (( i=0 ; i < $(arr_count dyn_$name) ; i++ ))
    do
        echo "dyn_$name[$i]: $(arr_at dyn_$name $i)"

    done
done

for name in "${array_names[@]}"
do
    echo "Dumping dyn_$name"
    for n in $(arr_get dyn_$name)
    do
        echo $n
    done
done

Pour plus de détails sur ces exemples, visitez Getting Bashed by Dynamic Arrays par Ludvik Jerabek


1
Je suis curieux de savoir pourquoi cela est rejeté. Y a-t-il quelque chose de mal / dangereux dans l'approche. Voudrait utiliser des fonctions sur bash <4.3.
stephenmm

J'apprécie votre message, j'ai trouvé des informations utiles et spécialement l'instruction "Get item count": eval echo \ $ {# $ my_variable_name [@]}
Daniel Perez

-1

en aucune façon :(

si vos tableaux sont aussi simples, utilisez des tableaux associatifs

    declare -A array
    array[A]="1 2 3"
    array[B]="a b c"

malheureusement, si vos tableaux sont plus compliqués (par exemple array=( "a b" c )), cela ne fonctionnera pas. Ensuite, vous devez réfléchir plus sérieusement à une autre façon d'atteindre votre objectif.


Quelle est la raison du downvote? Le tableau associatif fournit une belle façon de tout regrouper, en supposant que mes éléments ne contiendront pas tous d'espace.
Aaron

2
@Aaron En supposant que vos éléments ne contiennent pas d'espaces, c'est une conception raisonnable. @Watael Je suppose que commencer la réponse par «aucun moyen» alors que l'objectif principal de votre question est clairement possible n'était pas une bonne idée.
Gilles 'SO- arrête d'être méchant'

-1

Utilisation eval

arrayA=(1 2 3)
ARG=arrayA
eval echo \${$ARG[@]} # equivalent to eval echo \${arrayA[@]}
                      # note that we escape the first '$' to prevent from 
                      # its parameter expansion before passing it to echo
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.