Ассоциативные массивы в скриптах оболочки

11

Я видел трюк для реализации ассоциативных массивов в сценарии оболочки. Например, print array["apples"]может быть написано, echo \$array$keyгде ключ = яблоки.

Однако не было упоминания о том, как генерировать ключи для перебора массива. Единственный способ, которым я мог придумать, - хранить ключи в переменной, разделенной пробелами, чтобы я мог использовать цикл for для итерации по массиву.

Итак, есть ли другой способ хранения ключей для последующего использования?

Эрудит
источник
5
Если вы пытаетесь использовать ассоциативные массивы в сценарии оболочки, возможно, ваш проект слишком сложен для сценария оболочки :)
Мартин фон Виттих,
@MartinvonWittich почему? У меня есть сценарий оболочки, который выполняет сценарий SQL на одной из 3 возможных схем БД. Требуемая схема включена в имя файла с аббревиатурой. Мне нужно отображение между этой аббревиатурой и реальным именем схемы. Что может быть лучше, чем ассоциативный массив, учитывая, что фактические имена схем (не аббревиатура) могут различаться в разных средах, поэтому переменная массива (значения которой могут быть установлены только один раз) идеальна
Славян,
2
@Slav Я не спорю с ассоциативными массивами, а только со скриптами оболочки, где такая сложность необходима. Но это только мое личное предпочтение; Я часто ловлю себя на том, что начинаю писать сценарий оболочки, а затем сразу же переписываю его на Perl, когда понимаю, что превышаю определенный порог сложности.
Мартин фон Виттих,

Ответы:

20

Оболочки с ассоциативными массивами

Некоторые современные оболочки предоставляют ассоциативные массивы: ksh93, bash ≥4, zsh. В ksh93 и bash, если aэто ассоциативный массив, то "${!a[@]}"это массив его ключей:

for k in "${!a[@]}"; do
  echo "$k -> ${a[$k]}"
done

В zsh этот синтаксис работает только в режиме эмуляции ksh. В противном случае вы должны использовать собственный синтаксис zsh:

for k in "${(@k)a}"; do
  echo "$k -> $a[$k]"
done

${(k)a}также работает, если aнет пустого ключа.

В zsh вы также можете одновременно kвыполнять vциклы для eys и alues:

for k v ("${(@kv)a}") echo "$k -> $v"

Оболочки без ассоциативных массивов

Эмуляция ассоциативных массивов в оболочках, в которых их нет, - это гораздо больше работы. Если вам нужны ассоциативные массивы, возможно, пришло время добавить более крупный инструмент, такой как ksh93 или Perl.

Если вам нужны ассоциативные массивы в простой оболочке POSIX, вот способ имитировать их, когда ключи ограничены только символами 0-9A-Z_a-z(цифры ASCII, буквы и подчеркивание). При этом предположении ключи могут использоваться как часть имен переменных. Приведенные ниже функции воздействуют на массив, идентифицируемый префиксом именования «основа», который не должен содержать двух последовательных подчеркиваний.

## ainit STEM
## Declare an empty associative array named STEM.
ainit () {
  eval "__aa__${1}=' '"
}
## akeys STEM
## List the keys in the associatve array named STEM.
akeys () {
  eval "echo \"\$__aa__${1}\""
}
## aget STEM KEY VAR
## Set VAR to the value of KEY in the associative array named STEM.
## If KEY is not present, unset VAR.
aget () {
  eval "unset $3
        case \$__aa__${1} in
          *\" $2 \"*) $3=\$__aa__${1}__$2;;
        esac"
}
## aset STEM KEY VALUE
## Set KEY to VALUE in the associative array named STEM.
aset () {
  eval "__aa__${1}__${2}=\$3
        case \$__aa__${1} in
          *\" $2 \"*) :;;
          *) __aa__${1}=\"\${__aa__${1}}$2 \";;
        esac"
}
## aunset STEM KEY
## Remove KEY from the associative array named STEM.
aunset () {
  eval "unset __aa__${1}__${2}
        case \$__aa__${1} in
          *\" $2 \"*) __aa__${1}=\"\${__aa__${1}%%* $2 } \${__aa__${1}#* $2 }\";;
        esac"
}

(Предупреждение, непроверенный код. Обнаружение ошибок для синтаксически неверных основ и ключей не предусмотрено.)

Жиль "ТАК - перестань быть злым"
источник
5

Я не уверен, что вы подразумеваете под магазином, но вы можете перебирать ключи, используя ${!array[@]}синтаксис:

$ typeset -A foo=([key1]=bar [key2]=baz);
$ echo "${!foo[@]}" 
key2 key1

Итак, для повторения:

$ for key in "${!foo[@]}"; do echo "$key : ${foo[$key]}"; done
key2 : baz
key1 : bar

Я нашел хороший, короткий урок по этому вопросу здесь .


Как указано в комментариях ниже, ассоциативные массивы были добавлены в bashверсии 4. См здесь для журнальной статьи Linux на эту тему.

Тердон
источник
1
(bash version 4 only)Это важно отметить. Традиционно bashмассивы являются только числовыми.
Рики Бим
1
Возможно, вы захотите использовать typesetвместо declareсвоих примеров. Это сделало бы их переносимыми между bash 4 и ksh93, которые сначала реализовали ассоциативные массивы оболочки.
Jlliagre
0

Оболочки без ассоциативных массивов

Это не так сложно, когда ключи ограничены [0-9A-Za-z_](цифры, буквы, подчеркивание).

Хитрость заключается не в сохранении в массиве [ $ key ], а в хранении в переменных array_ $ key .

Установлен:

eval "array_$key='$value'"

Получить:

value=`eval echo '$'array_$key`

Примечание: значения не могут содержать '(одинарные кавычки).

Мариан Черны
источник
-1

это работает в Bash

cert="first"
web="second"
declare -A assoc_array=(["cert"]="${cert}" ["web"]="${web}")
echo "first is" ${assoc_array[cert]}
echo "second is" ${assoc_array[web]}

ИЛИ

#loop
for i in "${assoc_array[@]}"
do
   echo "$i"
done

Не нужно использовать eval afaik

JamesD
источник
1
Я считаю, что вы упустили суть вопроса.
G-Man говорит «Восстановить Монику»