Jak używać zmiennej jako części nazwy tablicy

11

Mam dwie tablice:

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

i chcę wydrukować jeden z nich za pomocą argumentu wiersza poleceń, tzn. bez żadnego if else .

Wypróbowałem kilka odmian składni bez powodzenia. Chcę zrobić coś takiego:

ARG="$1"

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

ale pojawia się błąd „złej zamiany”. Jak mogę to osiągnąć?

Aaron
źródło
To zdecydowanie nie idiomatyczne bash. Proszę nie rób tego.
Wildcard

Odpowiedzi:

22

Spróbuj to zrobić:

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

UWAGA

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

cyfra lub gdy po parametrze następuje znak, którego nie należy interpretować jako części jego nazwy.
* Jeśli pierwszym znakiem parametru jest wykrzyknik (!), Wprowadzany jest poziom zmiennej pośredniej. Bash używa wartości zmiennej utworzonej z reszty parametru jako nazwy zmiennej; zmienna ta jest następnie rozwijana i ta wartość jest używana w pozostałej części podstawienia, a nie w wartości samego parametru. Jest to znane jako ekspansja pośrednia. * Wyjątkami są rozszerzenia $ {! Prefiks *} i $ {! Name [@]} opisane poniżej. Wykrzyknik musi znajdować się zaraz po lewej nawiasach, aby wprowadzić pośrednie.

Gilles Quenot
źródło
Co dokładnie !robi się przed varzmienną? Jak to działa, wydawało się, że jest to podstawienie historii w google, ale nie widziałem, jak to działa.
Aaron
Zobacz mój zredagowany post
Gilles Quenot,
4

Chociaż możesz użyć dostępu pośredniego, jak wskazano w innej odpowiedzi , innym sposobem (w ksh i Bash 4.3 i nowszych) byłoby użycie namerefs. Zwłaszcza w przypadku tablic może to być bardziej przydatne, ponieważ można indeksować tablicę przez nazwę i nie trzeba umieszczać indeksu w zmiennej używanej jako odwołanie.

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

To nie działa poprzez pośredni dostęp:

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

Jak ująłby to programista C, ${!q[1]}tutaj działa jak qtablica wskaźników, a nie wskaźnik do tablicy.

ilkkachu
źródło
1
Działa to tylko w wersji bash ≥ 4.3.
G-Man mówi „Przywróć Monikę”
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

Uwaga: eskapuj cotes w przypadku miejsca !

eval dostuff \"\${array${1}[${2:-@}]}\"
Jonasz
źródło
1

Zajęło to wiele prób i błędów, ale ostatecznie zadziałało.

Czerpałem inspirację z Youness. Ale wszystkie inne odpowiedzi nie pomogły w moim starym bashu (suse11sp1 [wydanie 3.2.51 (1)])

Pętla „for” odmówiła rozwinięcia tablicy pośredniej, zamiast tego należy ją wstępnie rozwinąć, użyć jej do utworzenia kolejnej tablicy z nową nazwą zmiennej. Mój przykład poniżej pokazuje podwójną pętlę, ponieważ jest to moje zamierzone zastosowanie.

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

Używam #, aby usunąć „New_” z pierwszego wpisu tablicy, a następnie połączyć się z „Things”, aby uzyskać „FOOthings”. \ $ {} z echo i eval, a następnie wykonaj swoje czynności bez zgłaszania błędów, które są zawinięte w nową $ () i przypisane nowej nazwie zmiennej.

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

Niedawno odkryłem jeszcze jedną kwestię dotyczącą tego problemu. Utworzona zmienna nie jest w rzeczywistości tablicą, ale ciągiem rozdzielanym spacjami. W przypadku powyższego zadania było to w porządku, ponieważ ze względu na sposób działania „for” nie odczytuje tablicy, jest rozszerzana, a następnie zapętlana, patrz wyciąg poniżej:

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

Ale potem musiałem użyć go jako tablicy. W tym celu musiałem wykonać jeszcze jeden krok. Wziąłem dosłownie kod Dennisa Williamsona . Przetestowałem to i działa dobrze.

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

„IFS =”, ”to zmienna zawierająca ogranicznik. „czytaj” za pomocą „-a” odcina i podaje żądło z powrotem do zmiennej tablicowej. Uwaga, to nie ma szacunku dla cudzysłowie, ale istnieje kilka opcji w odczycie , aby poradzić sobie z tym, na przykład, że usunęliśmy flagi -r czego nie potrzebujesz. Więc połączyłem ten dodatek w tworzeniu zmiennych, co pozwala na przetwarzanie danych i adresowanie ich tak, jak powinno.

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
źródło
0

W ten sposób utworzyłbyś zmienną o nazwie dynamicznej (wersja 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[@]}

Poniżej znajduje się grupa funkcji, których można użyć do zarządzania tablicami o nazwach dynamicznych (wersja 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

Poniżej znajduje się grupa funkcji, których można użyć do zarządzania tablicami o nazwach dynamicznych (wersja 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

Aby uzyskać więcej informacji na temat tych przykładów, odwiedź stronę Getting Bashed by Dynamic Arrays autorstwa Ludvika Jerabka

NOPx90
źródło
1
Jestem ciekawy, dlaczego to zostało odrzucone. Czy w podejściu jest coś złego / niebezpiecznego? Chciałbym użyć funkcji na bash <4.3.
stephenmm
Doceniam twój post, znalazłem przydatne informacje, a zwłaszcza instrukcję „Uzyskaj liczbę przedmiotów”: eval echo \ $ {# $ moja_nazwa_zmiennej [@]}
Daniel Perez
-1

nie ma mowy :(

jeśli twoje tablice są takie proste, użyj tablic asocjacyjnych

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

niestety, jeśli twoje tablice są bardziej skomplikowane (na przykład array=( "a b" c )), to nie zadziałałoby. Następnie musisz się zastanowić nad innym sposobem na osiągnięcie celu.

watael
źródło
Jaki jest powód głosowania? Tablica asocjacyjna zapewnia dobry sposób grupowania wszystkiego, przy założeniu, że wszystkie moje elementy nie będą zawierały spacji.
Aaron
2
@Aaron Zakładając, że twoje elementy nie zawierają spacji, jest to rozsądny projekt. Atawatael Chyba rozpoczęcie odpowiedzi od „nie ma mowy”, kiedy skupienie się na twoim pytaniu jest wyraźnie możliwe, nie było dobrym pomysłem.
Gilles „SO- przestań być zły”
-1

Posługiwać się 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
źródło