Подтвердить что ты не робот

Создание ассоциативного массива в bash 3

После тщательного поиска способа создания ассоциативного массива в bash я обнаружил, что declare -A array выполнит трюк. Но проблема в том, что только для версии bash версии 4 и bash, которую сервер имеет в нашей системе, это 3.2.16.

Как я могу получить какой-то ассоциативный массив-хак в bash 3? Значения будут переданы в script как

ARG=array[key];

./script.sh ${ARG}

EDIT: Я знаю, что могу сделать это в awk или других инструментах, но для сценария, который я пытаюсь решить, необходим строгий bash.

4b9b3361

Ответ 1

Bash 3 не имеет ассоциативных массивов, поэтому вам придется использовать некоторые другие языковые функции для вашей цели. Обратите внимание, что даже в bash 4 код, который вы написали, не выполняет то, что вы утверждаете в нем: ./script.sh ${ARG} не передает ассоциативный массив дочернему script, потому что ${ARG} не расширяется до нуля, когда ARG является ассоциативным массивом. Вы не можете передать ассоциативный массив дочернему процессу, вам все равно нужно его кодировать.

Вам нужно определить протокол передачи аргументов между родительским script и дочерним script. Общим является передача аргументов в форме key=value. Это предполагает, что символ = не отображается в клавишах.

Вам также нужно выяснить, как представить ассоциативный массив в родительском script и в дочернем script. Они не должны использовать одно и то же представление.

Общим методом представления ассоциативного массива является использование отдельных переменных для каждого элемента с общим префиксом именования. Это требует, чтобы имя ключа состояло только из букв ASCII (любого из них), цифр и символов подчеркивания. Например, вместо ${myarray[key]} напишите ${myarray__key}. Если ключ определяется во время выполнения, вам нужно сначала округлить расширение: вместо ${myarray[$key]} напишите

n=myarray__${key}; echo ${!n}

Для назначения используйте printf -v. Обратите внимание на формат %s на printf, чтобы использовать указанное значение. Не пишите printf -v "myarray__${key}" %s "$value", так как это обрабатывало бы $value как формат и выполняло бы расширение printf % на нем.

printf -v "myarray__${key}" %s "$value"

Если вам нужно передать ассоциативный массив, представленный как это, дочернему процессу с представлением аргумента key=value, вы можете использовать ${!myarray__*} для перечисления всех переменных, имя которых начинается с myarray__.

args=()
for k in ${!myarray__*}; do
  n=$k
  args+=("$k=${!n}")
done

В дочернем процессе преобразовать аргументы формы key=value для разделения переменных с префиксом:

for x; do
  if [[ $x != *=* ]]; then echo 1>&2 "KEY=VALUE expected, but got $x"; exit 120; fi
  printf -v "myarray__${x%%=*}" %s "${x#*=}"
done

Кстати, вы уверены, что это то, что вам нужно? Вместо вызова bash script из другого bash script вместо этого вы можете запустить дочерний элемент script в подоболочке. Таким образом, он унаследовал бы от всех переменных родителя.

Ответ 2

Вот еще одно сообщение/объяснение ассоциативных массивов в bash 3 и старше с расширением параметра:
fooobar.com/questions/20287/...

Метод Gilles имеет приятный оператор if, чтобы улавливать проблемы с разделителями, дезинфицировать ввод oddball... и т.д. Используйте это.

Если вы знакомы с расширением параметра:
http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

Использовать в своем сценарии [как указано: отправка на script]: Script 1: sending_array.sh

# A pretend Python dictionary with bash 3 
ARRAY=( "cow:moo"
        "dinosaur:roar"
        "bird:chirp"
        "bash:rock" )

bash ./receive_arr.sh "${ARRAY[@]}"

Script 2: receive_arr.sh

argAry1=("[email protected]")

function process_arr () {
    declare -a hash=("${!1}")
    for animal in "${hash[@]}"; do
        echo "Key: ${animal%%:*}"
        echo "Value: ${animal#*:}"
    done
}

process_arr argAry1[@]

exit 0

Способ 2, поиск второго script: Script 1: sending_array.sh

source ./receive_arr.sh
# A pretend Python dictionary with bash 3 
ARRAY=( "cow:moo"
        "dinosaur:roar"
        "bird:chirp"
        "bash:rock" )

process_arr ARRAY[@]

Script 2: receive_arr.sh

function process_arr () {
    declare -a hash=("${!1}")
    for animal in "${hash[@]}"; do
        echo "Key: ${animal%%:*}"
        echo "Value: ${animal#*:}"
    done
}

Литература:
Передача массивов в качестве параметров в bash

Ответ 3

Вы можете записать пары ключ-значение в файл, а затем grep по ключу. Если вы используете шаблон типа

key=value

то вы можете egrep для ^key= сделать это довольно безопасным.

Чтобы "перезаписать" значение, просто добавьте новое значение в конец файла и используйте tail -1, чтобы получить только последний результат egrep

В качестве альтернативы вы можете поместить эту информацию в обычный массив, используя key=value как значение для массива, а затем итератор по массиву, чтобы найти значение.

Ответ 4

Если вы не хотите обрабатывать множество переменных, или ключи - это просто недопустимые идентификаторы переменных, и ваш массив должен иметь менее 256 элементов, вы можете злоупотреблять возвращаемыми значениями функции. Это решение не требует какой-либо подоболочки, поскольку это значение легко доступно как переменная или любая итерация, чтобы кричать производительность. Также он очень читается, почти как версия Bash 4.

Здесь самая базовая версия:

hash_index() {
    case $1 in
        'foo') return 0;;
        'bar') return 1;;
        'baz') return 2;;
    esac
}

hash_vals=("foo_val"
           "bar_val"
           "baz_val");

hash_index "foo"
echo ${hash_vals[$?]}

Подробнее и варианты в этом ответе

Ответ 5

Это оказывается смехотворно легким. Мне пришлось преобразовать bash 4 script, который использовал связку ассоциативных массивов с bash 3. Эти две вспомогательные функции сделали все:

array_exp() {
    exp=${@//[/__}
    eval "${exp//]}"
}

array_clear() {
    unset $(array_exp "echo \${!$1__*}")
}

Я ошеломлен тем, что это действительно работает, но что красота bash. Например.

((all[ping_lo] += counts[ping_lo]))

становится

array_exp '((all[ping_lo] += counts[ping_lo]))'

Или этот оператор печати:

printf "%3d" ${counts[ping_lo]} >> $return

становится

array_exp 'printf "%3d" ${counts[ping_lo]}' >> $return

Единственный синтаксис, который изменяется, - это очистка. Это:

counts=()

становится

array_clear counts

и вы настроены. Вы можете легко сказать array_exp, чтобы распознавать выражения типа "=()" и обрабатывать их, переписывая их как выражения array_clear, но я предпочитаю простоту этих двух функций.