Как использовать переменную как часть имени массива

11

У меня есть два массива:

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

и я хочу распечатать один из них, используя аргумент командной строки, т. е. без такового if else.

Я попробовал несколько вариантов синтаксиса, но безуспешно. Я хочу сделать что-то вроде этого:

ARG="$1"

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

но я получаю ошибку "плохая замена". Как мне этого добиться?

Аарон
источник
Это категорически не идиоматический удар. Пожалуйста, не делай этого.
Wildcard

Ответы:

22

Попробуйте сделать это:

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

НОТА

  • с man bash(расширение параметра):
    ${parameter}
           The value of parameter is substituted.
 The braces are required when parameter is a positional parameter with
  more than one

цифра, или когда за параметром следует символ, который не должен интерпретироваться как часть его имени.
* Если первый символ параметра является восклицательным знаком (!), Вводится уровень косвенной косвенности. Bash использует значение переменной, сформированной из остальной части параметра, в качестве имени переменной; эта переменная затем раскрывается, и это значение используется в остальной части замещения, а не в значении самого параметра. Это известно как косвенное расширение. * Исключениями являются расширения $ {! Prefix *} и $ {! Name [@]}, описанные ниже. Восклицательный знак должен следовать непосредственно за левой скобкой, чтобы ввести косвенность.

Жиль Квено
источник
Что именно !делает перед varпеременной? Как это работает, казалось, это замена истории на поиск в Google, но я не мог понять, как это работает здесь.
Аарон
Смотрите мое отредактированное сообщение
Gilles Quenot
4

Хотя вы можете использовать косвенный доступ, как указано в другом ответе , другим способом (в ksh и Bash 4.3 и новее) будет использование namerefs. Особенно в случае массивов это может быть более полезным, так как вы можете индексировать массив через nameref и не нужно помещать индекс в переменную, используемую в качестве ссылки.

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

Это не работает через косвенный доступ:

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

Как мог бы сказать программист C, ${!q[1]}здесь он действует так, как если бы он qбыл массивом указателей, а не указателем на массив.

ilkkachu
источник
1
Это работает только в bash версии ≥ 4.3.
G-Man говорит: «Восстановите Монику»
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

примечание: избегайте кавычек в случае пробела !

eval dostuff \"\${array${1}[${2:-@}]}\"
Ион
источник
1

Это заняло много проб и ошибок, но в итоге сработало.

Я черпал вдохновение у Юнесса. Но все остальные ответы не помогли на моем старом bash (suse11sp1 [3.2.51 (1) -release])

Цикл 'for' отказывался расширять косвенный массив, вместо этого вам нужно предварительно развернуть его, используйте его для создания другого массива с вашим новым именем переменной. Мой пример ниже показывает двойной цикл, так как это мое предназначение.

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

Я использую #, чтобы удалить «New_» из первой записи массива, а затем объединить с «вещи», чтобы получить «FOOthings». \ $ {} с помощью echo и eval, затем делайте свое дело по порядку, не выбрасывая ошибок, который оборачивается в новый $ () и присваивается имя новой переменной.

$ 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
ОБНОВЛЕНИЕ ##### 2018/06/07

Я недавно обнаружил еще одно вращение по этому вопросу. Созданная переменная на самом деле является не массивом, а строкой, разделенной пробелом. Для вышеприведенной задачи это было нормально, так как «for» работает, он не читает массив, он расширяется, а затем проходит цикл, см. Ниже выдержку:

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

Но тогда мне нужно было использовать его как массив. Для этого мне нужно было сделать еще один шаг. Я взял дословный код Денниса Уильямсона . Я проверил это, и он отлично работает.

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

"IFS = ','" - это переменная, содержащая ваш разделитель. «read» с «-a» обрезает и возвращает строчку обратно в переменную массива. Обратите внимание, что это не относится к кавычкам, но есть несколько опций для чтения , например, я удалил флаг -r, который мне не нужен. Таким образом, я теперь объединил это дополнение в создании переменной, которая позволяет обрабатывать и обрабатывать данные, как следует.

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
источник
0

Так вы бы создали переменную с динамическим именем (версия 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[@]}

Ниже приведена группа функций, которые могут использоваться для управления динамически именованными массивами (версия 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

Ниже приведена группа функций, которые можно использовать для управления динамически именованными массивами (версия 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

Для получения более подробной информации об этих примерах посетите раздел «Получение от динамических массивов» Людвика Джерабека.

NOPx90
источник
1
Мне любопытно, почему за это проголосовали. Есть ли что-то не так / опасно с подходом. Хотел бы использовать функции на bash <4.3.
Стивенм
Я ценю ваше сообщение, я нашел полезную информацию и особенно инструкцию "Получить количество элементов": eval echo \ $ {# $ my_variable_name [@]}
Даниэль Перес
-1

ни за что :(

если ваши массивы настолько просты, то используйте ассоциативные массивы

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

к сожалению, если ваши массивы более сложные (например array=( "a b" c )), это не сработает. Затем вам нужно подумать о другом способе достижения своей цели.

watael
источник
В чем причина понижения голосов? Ассоциативный массив обеспечивает хороший способ группировки всего, при условии, что все мои элементы не будут содержать пробелов.
Аарон
2
@ Аарон Предполагая, что ваши элементы не содержат пробелов, это разумный дизайн. Atawatael Полагаю, начинать ответ с «никоим образом», когда основная цель вашего вопроса явно возможна, было плохой идеей.
Жиль "ТАК - перестань быть злым"
-1

использование 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
источник