Come utilizzare una variabile come parte del nome di un array


11

Ho due matrici:

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

e voglio stamparne uno usando un argomento della riga di comando, cioè senza if else.

Ho provato alcune varianti della sintassi senza successo. Voglio fare qualcosa del genere:

ARG="$1"

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

ma ricevo un errore "sostituzione errata". Come posso raggiungere questo obiettivo?


Questo non è assolutamente un idiomatico bash. Per favore, non farlo.
Wildcard il

Risposte:


22

Prova a fare questo:

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

NOTA

  • da man bash(espansione dei parametri):
    ${parameter}
           The value of parameter is substituted.
 The braces are required when parameter is a positional parameter with
  more than one

cifra o quando il parametro è seguito da un carattere che non deve essere interpretato come parte del suo nome.
* Se il primo carattere del parametro è un punto esclamativo (!), Viene introdotto un livello di indiretta variabile. Bash utilizza il valore della variabile formata dal resto del parametro come nome della variabile; questa variabile viene quindi espansa e quel valore viene utilizzato nel resto della sostituzione, piuttosto che il valore del parametro stesso. Questo è noto come espansione indiretta. * Le eccezioni a questo sono le espansioni di $ {! Prefisso *} e $ {! Name [@]} descritte di seguito. Il punto esclamativo deve seguire immediatamente la parentesi graffa sinistra per introdurre il riferimento indiretto.


Cosa sta !facendo esattamente di fronte alla varvariabile? Come funziona, sembrava sostituire la storia su Google, ma non riuscivo a vedere come funzionasse qui.
Aaron,

Vedi il mio post modificato
Gilles Quenot

4

Mentre puoi usare l'accesso indiretto come indicato in un'altra risposta , un altro modo (in ksh e Bash 4.3 e successivi) sarebbe usare namerefs. Soprattutto nel caso degli array, questo può essere più utile poiché è possibile indicizzare l'array tramite nameref e non è necessario inserire l'indice nella variabile utilizzata come riferimento.

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

Questo non funziona attraverso l'accesso indiretto:

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

Come potrebbe dire un programmatore C, ${!q[1]}qui si comporta come se qfosse un array di puntatori, invece di essere un puntatore a un array.



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

nota: evita le cote in caso di spazio !

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

1

Ciò ha richiesto molti tentativi ed errori ma alla fine ha funzionato.

Ho preso ispirazione da Youness. Ma tutte le altre risposte non mi sono state d'aiuto nel mio vecchio bash (suse11sp1 [3.2.51 (1) -release])

Il ciclo 'for' ha rifiutato di espandere l'array indiretto, invece devi pre-espanderlo, usalo per creare un altro array con il tuo nuovo nome di variabile. Il mio esempio di seguito mostra un doppio ciclo, poiché quello è il mio uso previsto.

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

Sto usando # per eliminare "New_" dalla prima voce dell'array, quindi concatenandomi con "cose", per ottenere "FOOthings". \ $ {} con echo ed eval, quindi eseguono le loro operazioni in ordine senza generare errori, che viene racchiuso in un nuovo $ () e assegnato il nuovo nome di variabile.

$ 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
AGGIORNAMENTO ##### 2018/06/07

Di recente ho scoperto un altro giro su questo problema. La variabile creata non è in realtà un array, ma una stringa delimitata da spazi. Per il compito sopra questo era ok, a causa del modo in cui "for" funziona, non legge l'array, viene espanso e quindi ripetuto, vedi estratto sotto:

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

Ma poi avevo bisogno di usarlo come un array. Per questo avevo bisogno di eseguire un altro passo. Ho preso il codice alla lettera da Dennis Williamson . L'ho provato e funziona benissimo.

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

"IFS = ','" è una variabile che contiene il delimitatore. "read" con "-a" taglia e reinserisce la puntura nella variabile array. Nota, questo non ha rispetto per le virgolette, ma ci sono alcune opzioni in lettura per gestirlo, ad esempio ho rimosso il flag -r di cui non avevo bisogno. Quindi ho combinato questa aggiunta nella creazione della variabile, che consente di trattare e indirizzare i dati come dovrebbero.

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

-1

non c'è modo :(

se i tuoi array sono così semplici, usa array associativi

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

sfortunatamente, se le tue matrici sono più complicate (per esempio array=( "a b" c )), non funzionerebbe. Quindi, devi pensare di più a un altro modo per raggiungere il tuo obiettivo.


Qual è la ragione del downvote? L'array associativo offre un modo piacevole di raggruppare tutto, supponendo che i miei elementi non contengano spazio.
Aaron,

2
@Aaron Supponendo che i tuoi elementi non contengano spazi, questo è un design ragionevole. Atawatael Immagino che iniziare la risposta senza "alcun modo" quando l'obiettivo principale della tua domanda è chiaramente possibile non fosse una buona idea.
Gilles 'SO- smetti di essere cattivo'

-1

Uso 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

-1

Ecco come si crea una variabile con nome dinamico (versione 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[@]}

Di seguito è riportato un gruppo di funzioni che possono essere utilizzate per gestire array denominati dinamicamente (versione 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

Di seguito è riportato un gruppo di funzioni che possono essere utilizzate per gestire array denominati dinamicamente (versione 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

Per maggiori dettagli su questi esempi, visitare Getting Bashed by Dynamic Arrays di Ludvik Jerabek


Sono curioso di sapere perché questo viene annullato. C'è qualcosa di sbagliato / pericoloso nell'approccio. Vorrei usare le funzioni su bash <4.3.
stephenmm,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.