So verwenden Sie eine Variable als Teil eines Array-Namens

11

Ich habe zwei Arrays:

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

und ich möchte einen von ihnen mit einem Befehlszeilenargument ausdrucken, dh ohne if else.

Ich habe einige Variationen der Syntax ohne Erfolg ausprobiert. Ich möchte so etwas tun:

ARG="$1"

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

Ich erhalte jedoch den Fehler "schlechte Substitution". Wie kann ich das erreichen?

Aaron
quelle
Dies ist nachdrücklich keine idiomatische Bash. Bitte tu das nicht.
Wildcard

Antworten:

21

Versuchen Sie Folgendes:

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

HINWEIS

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

Ziffer oder wenn dem Parameter ein Zeichen folgt, das nicht als Teil seines Namens interpretiert werden soll.
* Wenn das erste Zeichen eines Parameters ein Ausrufezeichen (!) Ist, wird eine Ebene der variablen Indirektion eingeführt. Bash verwendet den Wert der aus dem Rest des Parameters gebildeten Variablen als Namen der Variablen. Diese Variable wird dann erweitert und dieser Wert wird im Rest der Substitution anstelle des Werts des Parameters selbst verwendet. Dies wird als indirekte Expansion bezeichnet. * Ausnahmen bilden die unten beschriebenen Erweiterungen von $ {! Prefix *} und $ {! Name [@]}. Das Ausrufezeichen muss unmittelbar auf die linke Klammer folgen, um eine Indirektion einzuführen.

Gilles Quenot
quelle
Was genau !macht man vor der varVariablen? Wie funktioniert das? Es schien ein Ersatz für die Geschichte beim Googeln zu sein, aber ich konnte nicht sehen, wie das hier funktionierte.
Aaron
Siehe meinen bearbeiteten Beitrag
Gilles Quenot
4

Während Sie den indirekten Zugriff wie in einer anderen Antwort angegeben verwenden können , besteht eine andere Möglichkeit (in ksh und Bash 4.3 und neuer) darin, namerefs zu verwenden. Insbesondere bei Arrays kann dies nützlicher sein, da Sie das Array über den Namen indizieren können und den Index nicht in die als Referenz verwendete Variable einfügen müssen.

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

Dies funktioniert nicht über den indirekten Zugriff:

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

Wie ein C-Programmierer es ausdrücken könnte, verhält sich ${!q[1]}hier so, als wäre qes ein Array von Zeigern, anstatt ein Zeiger auf ein Array zu sein.

ilkkachu
quelle
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

Hinweis: Escape Cotes bei Platz !

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

Dies erforderte viel Versuch und Irrtum, funktionierte aber schließlich.

Ich habe mich von Youness inspirieren lassen. Aber alle anderen Antworten haben bei meiner alten Bash nicht geholfen (suse11sp1 [3.2.51 (1) -release])

Die 'for'-Schleife hat sich geweigert, das indirekte Array zu erweitern. Stattdessen müssen Sie es vorab erweitern und damit ein weiteres Array mit Ihrem neuen Variablennamen erstellen. Mein Beispiel unten zeigt eine Doppelschleife, da dies meine beabsichtigte Verwendung ist.

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

Ich benutze #, um "New_" aus dem ersten Array-Eintrag zu löschen und dann mit "things" zu verketten, um "FOOthings" zu erhalten. \ $ {} mit echo und eval, dann mache ihre Sache in der richtigen Reihenfolge, ohne Fehler zu werfen, die in ein neues $ () eingeschlossen und mit dem neuen Variablennamen versehen werden.

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

Ich habe kürzlich einen weiteren Dreh zu diesem Thema entdeckt. Die erstellte Variable ist eigentlich kein Array, sondern eine durch Leerzeichen getrennte Zeichenfolge. Für die obige Aufgabe war dies in Ordnung, da "for" das Array nicht liest, es erweitert und dann durchlaufen wird, siehe Auszug unten:

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

Aber ich musste es dann als Array verwenden. Dazu musste ich noch einen Schritt ausführen. Ich habe wörtlich Code von Dennis Williamson genommen . Ich habe es getestet und es funktioniert gut.

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

Das "IFS = ','" ist eine Variable, die Ihren Begrenzer enthält. "read" mit "-a" schneidet und führt den Stich zurück in die Array-Variable. Beachten Sie, dass dies keine Berücksichtigung von Anführungszeichen hat, aber es gibt einige Optionen beim Lesen , um dies zu verwalten, z. B. habe ich das Flag -r entfernt, das ich nicht benötigte. Daher habe ich diesen Zusatz jetzt in der Variablenerstellung kombiniert, sodass die Daten so behandelt und adressiert werden können, wie sie sollten.

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
Stripy42
quelle
-1

auf keinen Fall :(

Wenn Ihre Arrays so einfach sind, verwenden Sie assoziative Arrays

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

Wenn Ihre Arrays (zum Beispiel array=( "a b" c )) komplizierter sind, würde das leider nicht funktionieren. Dann müssen Sie sich einen anderen Weg überlegen, um Ihr Ziel zu erreichen.

Watael
quelle
Was ist der Grund für die Ablehnung? Das assoziative Array bietet eine gute Möglichkeit, alles zu gruppieren, vorausgesetzt, meine Elemente enthalten keinen Platz.
Aaron
2
@Aaron Angenommen, Ihre Elemente enthalten keine Leerzeichen, ist dies ein vernünftiges Design. @Watael Ich denke, die Antwort mit „no way“ zu beginnen, wenn der Hauptfokus Ihrer Frage eindeutig möglich ist, war keine gute Idee.
Gilles 'SO - hör auf böse zu sein'
-1

Verwenden 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
MS.Kim
quelle
-1

Auf diese Weise würden Sie eine dynamisch benannte Variable erstellen (Bash-Version <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[@]}

Im Folgenden finden Sie eine Gruppe von Funktionen, mit denen dynamisch benannte Arrays verwaltet werden können (Bash-Version <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

Im Folgenden finden Sie eine Gruppe von Funktionen, mit denen dynamisch benannte Arrays verwaltet werden können (Bash-Version> = 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

Weitere Informationen zu diesen Beispielen finden Sie unter Getting Bashed by Dynamic Arrays von Ludvik Jerabek

NOPx90
quelle
Ich bin gespannt, warum dies abgelehnt wird. Stimmt etwas nicht mit dem Ansatz? Möchte Funktionen für bash <4.3 verwenden.
Stephenmm