Ссылка на переменные массива bash из другого массива

8

Я хочу написать скрипт для ссылки на несколько массивов из другого массива, который содержит имена переменных этих массивов.

Вот мой код до сих пор:

#!/bin/bash
array1=('array1string1' 'array1string2')
array2=('array2string1' 'array2string2')

array_names=('array1' 'array2')

for a in ${array_names[@]}
do
        for b in ${a[@]}
        do
                echo $b
        done
done

Я хотел бы, чтобы выходные данные сканировали оба массива (из внешнего цикла for) и печатали соответствующие строки во внутреннем цикле for, который вызывает echo. Мой текущий вывод просто показывает мне:

array1
array2

Буду благодарен за любые указания по этому поводу. Спасибо!

chnppp
источник
Есть ли причина, почему вы не можете просто сделать for b in "${array1[@]}" "${array2[@]}"; do ...; done?
Кусалананда
Я бы хотел, чтобы количество массивов было гибким. Поэтому, если я добавлю массив позже, я просто добавлю его в array_names и позволю циклу позаботиться об этом.
chnppp
Я думаю, что это случай для косвенного расширения. См. Stackoverflow.com/questions/8515411/… - но, в основном, переход ${a[@]}на ${!a}то, что вы хотите (я думаю).
17
1
@parkamark Нет, это просто дает ему первый элемент каждого массива. И ${!a[@]}дает длину массива a.
Кусалананда
Да, переход на ${!a}просто дает мне первые элементы.
chnppp

Ответы:

7

Bash 4.3 и более поздние версии поддерживают «ссылки на имена» или namerefs (похожая концепция существует ksh93, но область действия раздражающе отличается ):

#!/bin/bash

array1=('array1string1' 'array1string2')
array2=('array2string1' 'array2string2')

array_names=('array1' 'array2')

for a in "${array_names[@]}"; do
    declare -n arr="$a"

    for b in "${arr[@]}"; do
        echo "$b"
    done
done

Переменная arrявляется nameref, который действует как псевдоним для именованной переменной (переменная с именем $aв этом примере).

Без namerefs в более ранних версиях Bash одним из решений было бы создание нового массива, который содержал бы все элементы из других массивов:

all=( "${array1[@]}" "${array2[@]}" )

... немного похоже на array_namesмассив в вопросе, но с содержимым всех массивов, а затем перебрать "${all[@]}".

Также возможно использовать eval, но полученный код выглядит поразительно ужасно.

См . Ответ Гленна Джекмана о вариации с переменной косвенностью (введено в его текущей форме с версией 2 Bash).

Кусалананда
источник
3

@Kusalananda имеет лучший ответ для последних версий Bash. Для более ранних версий вы можете использовать косвенную переменную:

for a in ${array_names[@]}; do 
    tmp="${a}[@]"
    for b in "${!tmp}"; do echo "$b"; done     # or: printf "%s\n" "${!tmp}"
done

См. 4-й абзац https://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion.

Гленн Джекман
источник
0

Как вариант того, что было сказано:

#!/bin/bash

array1=('array1 string1' 'array1 string2')
array2=('array2 string1' 'array2 string2')
array_names=('array1' 'array2')

for (( i=0; i<${#array_names[@]}; i++ )); do

    declare -n arr="${array_names[i]}"

    for (( j=0; j<${#arr[@]}; j++ )); do
        echo "${arr[j]}"
    done

done

Вместо этого доступ к элементам по индексу

Крис
источник