Как перебирать массив с помощью косвенной ссылки?
Как я могу заставить этот код работать?
#!/bin/bash
ARRAYNAME='FRUITS'
FRUITS=( APPLE BANANA ORANGE )
for FRUIT in ${!ARRAYNAME[@]}
do
echo ${FRUIT}
done
Этот код:
echo ${!ARRAYNAME[0]}
Печать APPLE. Я пытаюсь сделать что-то подобное, но с "[@]" для итерации по массиву.
Спасибо заранее,
Ответы
Ответ 1
${!ARRAYNAME[@]}
означает "индексы ARRAYNAME
". Как указано в bash странице man, поскольку ARRAYNAME
установлен, но как строка, а не массив, он возвращает 0
.
Здесь решение с использованием eval
.
#!/usr/bin/env bash
ARRAYNAME='FRUITS'
FRUITS=( APPLE BANANA ORANGE )
eval array=\( \${${ARRAYNAME}[@]} \)
for fruit in "${array[@]}"; do
echo ${fruit}
done
То, что вы изначально пытались сделать, это создать косвенную ссылку. Они были представлены в версии bash версии 2 и были предназначены для значительной замены необходимости eval
при попытке добиться отражения в оболочке.
Что вы должны делать при использовании косвенных ссылок с массивами, это включить [@]
в ваше предположение при имени переменной:
#!/usr/bin/env bash
ARRAYNAME='FRUITS'
FRUITS=( APPLE BANANA ORANGE )
array="${ARRAYNAME}[@]"
for fruit in "${!array}"; do
echo $fruit
done
Все, что сказало, одно дело использовать косвенные ссылки в этом тривиальном примере, но, как указано в ссылке, предоставленной Деннисом Уильямсоном, вы должны не решаться использовать их в реальных сценариях. Они почти гарантированно делают ваш код более запутанным, чем необходимо. Обычно вы можете получить необходимую функциональность с помощью ассоциативного массива.
Ответ 2
Вот способ сделать это без eval.
Смотрите Bash трюк # 2, описанный здесь:
http://mywiki.wooledge.org/BashFAQ/006
Кажется, работает в bash 3 и выше.
#!/bin/bash
ARRAYNAME='FRUITS'
tmp=$ARRAYNAME[@]
FRUITS=( APPLE BANANA ORANGE "STAR FRUIT" )
for FRUIT in "${!tmp}"
do
echo "${FRUIT}"
done
Вот более реалистичный пример, показывающий, как передать массив по ссылке на функцию:
pretty_print_array () {
local arrayname=$1
local tmp=$arrayname[@]
local array=( "${!tmp}" )
local FS=', ' # Field seperator
local var
# Print each element enclosed in quotes and separated by $FS
printf -v var "\"%s\"$FS" "${array[@]}"
# Chop trailing $FS
var=${var%$FS}
echo "$arrayname=($var)"
}
FRUITS=( APPLE BANANA ORANGE "STAR FRUIT" )
pretty_print_array FRUITS
# prints FRUITS=("APPLE", "BANANA", "ORANGE", "STAR FRUIT")
Ответ 3
Я просто хотел добавить еще один полезный случай использования. Я искал в Интернете решение для другой, но связанной проблемы
ARRAYNAME=( FRUITS VEG )
FRUITS=( APPLE BANANA ORANGE )
VEG=( CARROT CELERY CUCUMBER )
for I in "${ARRAYNAME[@]}"
do
array="${I}[@]"
for fruit in "${!array}"; do
echo $fruit
done
done
Ответ 4
Несмотря на простой вопрос с ОП, эти ответы не будут масштабироваться для наиболее распространенных реальных случаев использования, т.е. элементов массива, содержащих пробельные символы или подстановочные знаки, которые еще не должны быть расширены до имен файлов.
FRUITS=( APPLE BANANA ORANGE 'not broken' '*.h')
ARRAYNAME=FRUITS
eval ARRAY=\(\${$ARRAYNAME[@]}\)
$ echo "${ARRAY[4]}"
broken
$ echo "${ARRAY[5]}"
config.h
$
Это работает:
FRUITS=( APPLE BANANA ORANGE 'not broken' '*.h')
ARRAYNAME=FRUITS
eval ARRAY="(\"\${$ARRAYNAME[@]}\")"
$ echo "${ARRAY[3]}"
not broken
$ echo "${ARRAY[4]}"
*.h
$
Так же, как вы привыкли использовать "[email protected]"
not [email protected]
, всегда указывайте внутри ( )
для расширений массива, если вы не хотите расширения имени файла или не знаете возможности элементов массива, содержащих пробелы.
Сделайте это: X=("${Y[@]}")
Не это: X=(${Y[@]})
Ответ 5
eval выполняет код, содержащий элементы массива, даже если они содержат, например, подстановки команд. Он также изменяет элементы массива, интерпретируя в них bash метасимволы.
Единственный инструмент, который позволяет избежать этих проблем, - это ссылка:
#!/bin/bash
declare -n ARRAYNAME='FRUITS'
FRUITS=(APPLE BANANA ORANGE "BITTER LEMON")
for FRUIT in "${ARRAYNAME[@]}"
do
echo "${FRUIT}"
done