Я получаю ошибку «неверной замены» при попытке получить именованный ключ из массива с динамическими ссылками.
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] ...]
Есть ли причина не устанавливать более новую версию bash?
Чепнер, Спасибо за разъяснения. Эд Мортон, я использую версию, предоставленную моим провайдером общего хостинга (Godaddy).



Вы можете использовать косвенную ссылку:
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]". Однако каждый раз требуется дополнительный шаг по сохранению этой строки. И все же это приятно!
@PaulHodges Действительно, я использовал $database, потому что он уже есть в ОП.
О, я понял. Вообще не критиковал. Просто неуклюже пытаюсь это расширить. :) Я не думаю, что использовал именно это конкретное применение ссылки на элемент ассоциативного массива и, так сказать, «думал вслух» и думал, что эта перспектива может быть полезна кому-то другому. Вот и все.
Поскольку ОП сказал, что их версия bash не поддерживает ссылки на имена, я предположил, что она также не поддерживает косвенные переменные (ИМХО, почти невозможно найти в Google, какая версия bash представила ту или иную функцию), поэтому полезно знать, что косвенные переменные действительно существовали в первую очередь .
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.
databasesне является массивом массивов; это просто массив имен массивов.