Вопрос по bash – Ассоциативные массивы являются локальными по умолчанию

26

Ассоциативные массивы кажутся локальными по умолчанию при объявлении внутри тела функции, где они должны быть глобальными. Следующий код

#!/bin/bash

f() {
    declare -A map
    map[x]=a
    map[y]=b
}

f
echo x: ${map[x]} y: ${map[y]}

производит вывод:

x:  y:

пока это

#!/bin/bash

declare -A map

f() {
    map[x]=a
    map[y]=b
}

f
echo x: ${map[x]} y: ${map[y]}

производит вывод:

x: a y: b

Можно ли объявить глобальный ассоциативный массив внутри функции? Или какой обходной путь можно использовать?

Ваш Ответ

4   ответа
19


Отправлено: вторник, 23 августа 2011 г. 06:53:27 -0700
Subject: Re: YAQAGV (еще один вопрос о глобальных переменных)

@bash 4.2 добавлено «объявление -g» для создания глобальных переменных внутри функции.

Спасибо, Грег! Однако Debian Squeeze все еще имеет Bash 4.1.

declare -g также будет работать на Zsh:) AdrieanKhisbe
5

4.2 добавляет «Declare -g», но он содержит ошибки для ассоциативных массивов, поэтому он (пока) не отвечает на вопрос. Вот мой отчет об ошибках и подтверждение Чета, что на следующий выпуск запланировано исправление.

http: //lists.gnu.org/archive/html/bug-bash/2013-09/msg00025.htm

Но я по счастливой случайности нашел обходной путь, вместо того, чтобы объявлять массив и присваивать ему начальное значение одновременно, сначала объявить массив, а затем выполнить присваивание. То есть не делайте этого:

declare -gA a=([x]=1 [y]=2)

но это вместо этого:

declare -gA a; a=([x]=1 [y]=2)
7

ash <4.2 - объявить массив вне функции.

f() {
   map[y] = foo
}

declare -A map
foo
echo "${map[y]}"
та опция явно показана в моем собственном примере. Это не работает, однако, когда вам нужно «сбросить», а затем «объявить» ту же переменную внутри функции. Но тем не менее, для Bash <4.2 существует обходной путь, заключающийся в том, чтобы «эхо» перевести объявление переменной из функции во внешнее «eval» с доступом к основной среде. davide
0

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

get_array(){
   local arr_name="$1"
   local arr_key="$2"

   arr_namekey_var="ASSOCARRAY__${arr_name}__${arr_key}"
   echo "${!arr_namekey_var:=}"
}

set_array(){
   local arr_name="$1"
   local arr_key="$2"
   local arr_value="$3"

   arr_namekey_var="ASSOCARRAY__${arr_name}__${arr_key}"
   if [[ -z "${arr_value}" ]]; then
      eval ${arr_namekey_var}=
   else
      printf -v "${arr_namekey_var}" "${arr_value}"
   fi
}

Несколько заметок:

Имя массива и ключ массива можно объединить в одно значение, но на практике разбиение оказалось удобным.__ в качестве разделителя может быть взломан злонамеренным или неосторожным использованием - чтобы быть в безопасности, используйте только одинарные значения подчеркивания в имени массива и ключе, а не только буквенно-цифровые значения. Конечно, состав внутренней переменной (разделители, префиксы, суффиксы ...) можно настроить в соответствии с потребностями приложений и разработчиков. Расширение значения по умолчанию гарантирует, что неопределенный ключ массива (а также имя массива!) Расширится до нулевой строки. Как только вы переходите на версию bash, где вам удобны встроенные ассоциативные массивы, эти две процедуры можно использовать как обертки для реальных ассоциативных массивов без необходимости рефакторинга всей кодовой базы.

Похожие вопросы