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

Я получаю ошибку «неверной замены» при попытке получить именованный ключ из массива с динамическими ссылками.

GNU Bash версии 4.1.2(2)

РАБОТАЕТ:

declare -rA DBONE=([system_name]='ABC' [dbname]='abc_database' [dbusername]='user1')
declare -rA DBTWO=([system_name]='DEF' [dbname]='def_database' [dbusername]='user2')
declare -rA DBTHREE=([system_name]='XYZ' [dbname]='xyz_database' [dbusername]='user3')

# works: get value when using the real name of the array, e.g. "DBTWO"
echo ${DBTWO["dbname"]}

Вывод: «def_database» (правильно)

ОШИБКА: неверная замена.

declare -rA DBONE=([system_name]='ABC' [dbname]='abc_database' [dbusername]='user1')
declare -rA DBTWO=([system_name]='DEF' [dbname]='def_database' [dbusername]='user2')
declare -rA DBTHREE=([system_name]='XYZ' [dbname]='xyz_database' [dbusername]='user3')

# create an array of the arrays
declare -a databases=(DBONE DBTWO DBTHREE)

# get the first array (e.g. DBONE)
database = "${databases[0]}"
echo "The dbname is ${$database[dbname]}"
echo "The dbname is ${$database}[dbname]"

Выход:

./myscript.sh: ${$database[dbname]}: bad substitution
./myscript.sh: ${$database}[dbname]: bad substitution

В конечном счете, моя конечная цель — перебрать массив массивов, например так:

# create an array of the arrays
declare -a databases=(DBONE DBTWO DBTHREE)
for ((i=0; i<"${#databases[*]}"; i++)); do
    database = "${databases[$i]}"
    dbname = "${database[dbname]}"
    echo "The database is $database, and the dbname is $dbname"
done    

Примечание: моя версия bash (GNU Bash версии 4.1.2(2)) не поддерживает declare "-n". Он допускает следующие варианты: declare: usage: declare [-aAfFilrtux] [-p] [name[=value] ...]

databases не является массивом массивов; это просто массив имен массивов.
chepner 07.06.2024 13:45

Есть ли причина не устанавливать более новую версию bash?

Ed Morton 07.06.2024 14:17

Чепнер, Спасибо за разъяснения. Эд Мортон, я использую версию, предоставленную моим провайдером общего хостинга (Godaddy).

Genki 07.06.2024 23:14
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
1
3
66
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вы можете использовать косвенную ссылку:

declare -rA DBONE=([system_name]='ABC' [dbname]='abc_database' [dbusername]='user1')
declare -rA DBTWO=([system_name]='DEF' [dbname]='def_database' [dbusername]='user2')
declare -rA DBTHREE=([system_name]='XYZ' [dbname]='xyz_database' [dbusername]='user3')

declare -a databases=(DBONE DBTWO DBTHREE)
for ((i=0; i<"${#databases[*]}"; i++)); do
    database = "${databases[$i]}"
    ref = "$database[dbname]"
    dbname = "${!ref}"
    echo "The database is $database, and the dbname is $dbname"
done

Интересно отметить, что $ref — это просто строка DBONE[dbname] со встроенными квадратными скобками, а НЕ какой-либо собственный поиск, но при использовании в качестве ссылочного ключа он работает нормально. Вы могли бы сказать это буквально ref = "${databases[0]}[dbname]". Однако каждый раз требуется дополнительный шаг по сохранению этой строки. И все же это приятно!

Paul Hodges 07.06.2024 17:40

@PaulHodges Действительно, я использовал $database, потому что он уже есть в ОП.

Philippe 07.06.2024 20:32

О, я понял. Вообще не критиковал. Просто неуклюже пытаюсь это расширить. :) Я не думаю, что использовал именно это конкретное применение ссылки на элемент ассоциативного массива и, так сказать, «думал вслух» и думал, что эта перспектива может быть полезна кому-то другому. Вот и все.

Paul Hodges 07.06.2024 21:50

Поскольку ОП сказал, что их версия bash не поддерживает ссылки на имена, я предположил, что она также не поддерживает косвенные переменные (ИМХО, почти невозможно найти в Google, какая версия bash представила ту или иную функцию), поэтому полезно знать, что косвенные переменные действительно существовали в первую очередь .

Ed Morton 08.06.2024 01:16

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

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

#! /usr/bin/env bash

declare -rA db=( [system_name:DB1]='ABC' [dbname:DB1]='abc_database' [dbusername:DB1]='user1'
                 [system_name:DB2]='DEF' [dbname:DB2]='def_database' [dbusername:DB2]='user2'
                 [system_name:DB3]='XYZ' [dbname:DB3]='xyz_database' [dbusername:DB3]='user3' )

declare -ra databases=( DB1 DB2 DB3 )

setdata(){
  local dbkey = "$1";
  declare -g dbname=${db["dbname:$dbkey"]}
  declare -g username=${db["dbusername:$dbkey"]}
  declare -g system_name=${db["system_name:$dbkey"]}
}

echo "With key variables -"
field=dbname; database = "${databases[0]}"; key = "$field:$database"
printf '\n%s\n' "The $field for $database is ${db[$key]}"

printf '\nRaw access -\n'
printf '\n%s\n' "The system_name for ${databases[1]} is ${db[system_name:${databases[1]}]}"

printf '\nLooping with function:\n\n'

for database in "${databases[@]}"
do setdata $database
   echo "$database: dbname=$dbname username=$username system_name=$system_name"
done
echo

В использовании:

$: ./tst
With key variables -

The dbname for DB1 is abc_database

Raw access -

The system_name for DB2 is DEF

Looping with function:

DB1: dbname=abc_database username=user1 system_name=ABC
DB2: dbname=def_database username=user2 system_name=DEF
DB3: dbname=xyz_database username=user3 system_name=XYZ

Если вы можете использовать более новую версию bash, использование namerefs станет очень простым:

for name in DB{ONE,TWO,THREE}; do
  declare -n ref = "$name"
  printf 'array: %s; system: %s; db: %s\n' "$name" "${ref[system_name]}" "${ref[dbname]}"
done
array: DBONE; system: ABC; db: abc_database
array: DBTWO; system: DEF; db: def_database
array: DBTHREE; system: XYZ; db: xyz_database

Я не помню, были ли namerefs введены в версии 4.4 или 5.0.

Другие вопросы по теме