Як використовувати змінну як частину імені масиву


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 використовує значення змінної, утвореної з решти параметра, як ім'я змінної; Потім ця змінна розширюється, і це значення використовується в решті підстановки, а не значення самого параметра. Це відомо як непряме розширення. * Виняток з цього - розширення $ {! Префікса *} та $ {! Ім'я [@]}, описані нижче. Знак оклику повинен негайно слідувати лівій дужці, щоб ввести непряму.


Що саме робиться !перед varзмінною? Як це працює, здавалося, що це заміна історії в Google, але я не бачив, як це працює тут.
Аарон

Дивіться мою відредаговану публікацію
Жилль Кінот

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це масив покажчиків, а не як вказівник на масив.



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])

Цикл "для" відмовився розширювати непрямий масив, замість цього потрібно попередньо розгорнути його, використовуючи його для створення іншого масиву з новим ім'ям змінної. Мій приклад нижче показує подвійну петлю, оскільки це моє призначення.

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_" з першого запису масиву, потім з'єднавшись із "things", щоб отримати "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

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

Більш детально про ці приклади відвідайте Getting Bashed by Dynamic Arrays Ludvik Jerabek


1
Мені цікаво, чому це стає недозволеним. Чи є щось неправильне / небезпечне з підходом. Хочеться використовувати функції на bash <4.3.
stephenmm

Я високо ціную вашу публікацію, я знайшов корисну інформацію та спеціально інструкцію "Отримати кількість предметів": eval echo \ $ {# $ my_variable_name [@]}
Даніель Перес

-1

у жодному разі :(

якщо ваші масиви такі прості, то використовуйте асоціативні масиви

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

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


У чому причина спустошення? Асоціативний масив забезпечує хороший спосіб групування всього, припускаючи, що всі мої елементи не містять місця.
Аарон

2
@Aaron Припускаючи, що ваші елементи не містять пробілів, це розумний дизайн. @Watael Я думаю, починати відповідь “ніяк”, коли чітко можливий основний фокус вашого питання, не було гарною ідеєю.
Жил "ТАК - перестань бути злим"

-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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.