Вопрос или проблема
У меня есть массив
declare -a arr0=("'1 2 3'" "'4 5 6'")
и переменная
x=0
Затем я создаю новую переменную с именем массива
tmp="arr$x"
и я хотел бы иметь возможность расширять содержимое arr0
из этой переменной tmp
вот так
newArr=( "${!tmp}" )
и использовать newArr
как обычный массив, например, использовать индексы и т. д.
Но когда я пытаюсь его напечатать сейчас, это выглядит так:
$ echo ${newArr[@]}
'1 2 3'
Хранится только первый элемент, и я не знаю, как это исправить.
Я также пытался создать newArr
вот так
newArr=( "${!tmp[@]}" )
но тогда это еще хуже – только 0 печатается.
$ echo ${newArr[@]}
0
Итак, знаете ли вы, как использовать массив, если его имя хранится в другой переменной?
Это возможно с помощью eval:
$ declare -a array=( 1 2 3 4 )
$ echo "${array[@]}"
1 2 3 4
$ p=ay
$ tmp=arr$p
$ echo "$tmp"
array
$ echo "\${${tmp}[@]}"
${array[@]}
$ echo "newarray=(\"\${${tmp}[@]}\")"
newarray=("${array[@]}")
$ eval "newarray=(\"\${${tmp}[@]}\")"
$ echo "${newarray[@]}"
1 2 3 4
$
Команды, начинающиеся с echo, предназначены для иллюстрации, eval опасен.
Обратите внимание, что вышеуказанное не сохраняет индексы массивов для разреженных массивов.
Косвенное расширение имеет некоторые исключения, и использование ! в массивах является одним из исключений.
Из man bash:
Если первый символ параметра – восклицательный знак (!), вводится
уровень косвенности переменной. Bash использует значение переменной,
образованное из оставшейся части параметра как имя переменной; эта
переменная затем расширяется, и это значение используется в остальной
подстановке, а не значение самого параметра. Это известно как косвенное
расширение.Исключением из этого являются расширения ${!prefix*} и ${!name[@]}
описанные ниже. ${!prefix*} Имена, совпадающие с префиксом. Разворачивается
в имена переменных, чьи имена начинаются с префикса, разделенные первым
символом специальной переменной IFS.
Как описано в BASH FAQ06 , одно из решений выглядит так:
arrA=("AA" "2" "4")
echo -e "массив arrA содержит: \c" && declare -p arrA
ref=arrA;
tmp=${ref}[@] #это можно настроить на [1], [2] и т.д. для ссылки на конкретные элементы массива
echo "Косвенная печать: ${!tmp}"
#Вывод
массив arrA содержит: declare -a arrA='([0]="AA" [1]="2" [2]="4")'
Косвенная печать: AA 2 4
bash
4.3 добавил поддержку ksh93
-подобных namerefs.
Итак, в bash-4.3 или выше вы можете сделать:
a0[5]=something
x=0
typeset -n var="a$x"
printf '%s\n' "${var[5]}"
Но обратите внимание, что это ссылка (указатель, а не копия) на имя переменной, а не переменной (разница имеет значение, когда у вас несколько переменных с одинаковым именем в разных контекстах, например, для локальных переменных в функциях).
bash
скопировал массивы ksh
с их неуклюжей конструкцией. Создание копии массива в bash
сложно, вы можете использовать вспомогательную функцию, такую как:
copy_array() { # Аргументы: <имя_исх_массива> <имя_целе_массива>
eval '
local i
'"$2"'=()
for i in "${!'"$1"'[@]}"; do
'"$2"'[$i]=${'"$1"'[$i]}
done'
}
Чтобы использовать, например:
$ a0[5]=123
$ x=0
$ copy_array "a$x" var
$ typeset -p var
declare -a var=([5]="123")
ksh
(и bash
, который скопировал ksh
) – единственные оболочки, где массивы разрежены (или ассоциативные массивы, ключи которых ограничены положительными числами) (также это единственные массивы, с индексами, начинающимися с 0 вместо 1, или где $array
неразвернуто не расширяется до элементов, а до элемента с индексом 0). Это намного проще с другими оболочками.
rc
:array_copy = $array
fish
:set array_copy = $array
csh
:set array_copy = ($array:q)
zsh
:array_copy=("$array[@]")
yash
:array_copy=("$array")
zsh
илиyash
:array_copy=("${array[@]}"}
Для косвенной копии (где $var
содержит имя исходного массива):
rc
:eval array_copy = '$'$var
fish
:eval set array_copy \$$var
csh
:eval "set array_copy = (\$${var}:q)"
zsh
:array_copy=("${(@P)var}")
yash
(илиzsh
):eval 'array_copy=("${'"$var"'[@]}")'
Ответ или решение
Вопрос о развитии содержимого массива через его имя, представленный на форуме, уже имеет множество решений, однако, я постараюсь предложить более формализованный и подробный ответ на эту проблему. Решение в первую очередь связано с особенностями работы с массивами в оболочке bash
и проблемами, возникающими при попытке косвенной индексации массивов.
Проблема
У вас есть массив arr0
и переменная x=0
. Вы хотите получить доступ к содержимому массива, используя временное имя tmp="arr$x"
, и затем сохранить это содержимое в новом массиве newArr
, чтобы работать с ним через индексы.
Решение через eval
eval
может использоваться для осуществления косвенной индексации массива в bash
. Однако, он считается небезопасным инструментом и его стоит применять с осторожностью, поскольку неправильное его использование может привести к неожиданному выполнению команд.
Вот пример кода для решения описанной задачи:
declare -a arr0=("'1 2 3'" "'4 5 6'")
x=0
tmp="arr$x"
eval "newArr=(\"\${$tmp[@]}\")"
echo "${newArr[@]}"
Объяснение
-
Определение и инициализация:
declare -a arr0=("'1 2 3'" "'4 5 6'")
создает массивarr0
.x=0
устанавливает значение переменнойx
.
-
Создание переменной-ссылки:
tmp="arr$x"
формирует имя массива, используя значениеx
.
-
Использование
eval
:eval
используется для динамического построения команды, которая выполняет косвенное обращение к значению переменнойtmp
как к имени массива.
-
Массив
newArr
:- Новый массив
newArr
создается с использованием значения, полученного из массиваarr0
.
- Новый массив
Запасное решение через typeset
(для Bash 4.3+)
С bash
начиная с версии 4.3, вы можете использовать typeset -n
, чтобы создать ссылку на массив:
declare -a arr0=("'1 2 3'" "'4 5 6'")
x=0
typeset -n newArr="arr$x"
echo "${newArr[@]}"
Заключение
Работа с переменными, именуемыми косвенно, требует определенного уровня понимания специфики вашей среды разработчика и использует знания о различных механизмах, доступных в bash
. Оба представленных решения обладают своими преимуществами, но работа через eval
требует дополнительного внимания к безопасности кода. Копирование массива, использование именованных ссылок и работа с массивами в других оболочках представляют собой альтернативные подходы, которые зависят от вашей конкретной среды разработки и ограничений.