#arrays #bash #variables #global-variables #associative-array
#массивы #bash #переменные #глобальные переменные #ассоциативный-массив
Вопрос:
В настоящее время у нас есть настройка, чтобы объявить несколько переменных в файле конфигурации, а затем мы запускаем некоторые процессы, которые зависят от этих переменных. Дополнительная функциональность, которую я пытаюсь предоставить, — это скрипт default_variables, который будет присваивать значения по умолчанию всем переменным в файлах конфигурации, не переопределяя ничего, что объявлено в файле конфигурации.
Вот мой текущий код:
#!/bin/bash -x
# allows var to be used as an associative array
declare -A var
# variable declarations must be of the form:
# var[var_name]=var_value
# after processing loop, the above will be evaluated as:
# export var_name=var_value
# declare default variables
var[a]=a_val
var[b]=b_val
var[c]=c_val
var[d]=d_val
# iterate on associative array indices
for default_var in `echo "${!var[@]}"`
do
test_var=$${default_var}
# only export variables that have not been previously declared
if [[ -z `eval $test_var` ]]
then
# export each index name as a variable
export $default_var=${var[$default_var]}
fi
done
Ошибка, которую я получаю в настоящее время, заключается в том, что оператор eval пытается выполнить значение переменной, если оно было объявлено до запуска скрипта. Например, если приведенный выше сценарий был запущен дважды, ошибка будет:
-sh: a_val: command not found
-sh: b_val: command not found
-sh: c_val: command not found
-sh: d_val: command not found
Теперь я определенно ищу более элегантный способ решения этой проблемы, но это лучшее, что я мог придумать. Наконец, я знаю, что обычный экспорт значений и проверка каждой переменной по отдельности на предмет их выделения были бы более «правильным» способом сделать это, но я не хочу экспоненциально раздувать сценарий, и я не хочу, чтобы людям приходилось копировать логику каждый раз, когда они хотят добавитьновая переменная в скрипте, и я не могу просто запустить скрипт default_variables перед файлом конфигурации, позволяя ему переопределять значения по умолчанию, потому что некоторые значения по умолчанию зависят от значений конфигурации.
Заранее спасибо за любую помощь!
Ответ №1:
Не использовать eval
, что приводит к выполнению его аргумента как команды. Все, что вам нужно, это значение переменной с именем by $default_var
. Существует отличный синтаксис для косвенной ссылки на переменную:
if [[ -z ${!default_var} ]]
Хотя вы действительно хотели проверить наличие непустого. -z
тесты на пустоту. Так и должно быть.
if [[ -n ${!default_var} ]]
Кроме того, это слишком сложно и, возможно, неверно:
for default_var in `echo "${!var[@]}"`
Просто напишите:
for default_var in "${!var[@]}"
Комментарии:
1. Верно по всем пунктам, сэр! За исключением параметра -n, я хотел проверить пустоту, потому что, если она пуста, я хочу присвоить значение, в противном случае пропустить его, но $ {! default_var} выполнил задание, и команда echo действительно не понадобилась. Я пытался быть более осторожным в своем первом проекте сценария, так как я вкладывал переменные 🙂
2. Теперь я вижу, что комментарий, который я поставил, на самом деле вводил в заблуждение относительно моих намерений в отношении -n / -z . Я изменил его, чтобы сказать «только экспортируйте переменные, которые не были объявлены ранее»
Ответ №2:
#!/bin/bash
# allows var to be used as an associative array
declare -A var
# variable declarations must be of the form:
# var[var_name]=var_value
# after processing loop, the above will be evaluated as:
# export var_name=var_value
# declare default variables
var[a]=a_val
var[b]=b_val
var[c]=c_val
var[d]=d_val
# iterate on associative array indices
for default_var in "${!var[@]}"; do #<=== no ugly `echo ...` needed
[[ "${!default_var}" ]] amp;amp; continue #<=== indirect expansion
# export each index name as a variable
export "$default_var=${var[$default_var]}" #<=== use quotes!
done
echo "a=$a"
echo "b=$b"
echo "c=$c"
echo "d=$d"
Я вызвал этот скрипт banana
:
$ ./banana
a=a_val
b=b_val
c=c_val
d=d_val
$ a=gorilla ./banana
a=gorilla
b=b_val
c=c_val
d=d_val
Теперь это проверяет только, является ли переменная неустановленной или пустой, то есть пользователь не может явно сделать переменную пустой:
$ a= ./banana
a=a_val
b=b_val
c=c_val
d=d_val
Чтобы поддержать это, вы можете вместо этого сделать:
for default_var in "${!var[@]}"; do #<=== no ugly `echo ...` needed
if ! declare -p "$default_var" amp;>/dev/null; then
# export each index name as a variable
export "$default_var=${var[$default_var]}" #<=== use quotes!
fi
done
так что:
$ a= ./banana
a=
b=b_val
c=c_val
d=d_val
Ответ №3:
Вот как я бы это сделал:
# iterate on associative array indices (no need to do `echo ...`)
for default_var in "${!var[@]}"
do
# skip variables that have been previously declared (even if their value is null)
if [[ -z ${!default_var .} ]]
then
# export each index name as a variable (the safer way with quotes)
export "$default_var=${var[$default_var]}"
fi
done
Комментарии:
1.
[[ ! -v $default_var ]]
. fwiw.2. @rici Верно, но только для запуска bash 4.2
3. Ага. Всего треть десятилетия. Конечно, все еще есть те, кто считает C99 слишком новомодным для использования. 🙂
4. @rici Я надеюсь, вы не шутите, предполагая, что все пользователи легко обновляют свои системы.
5. Совсем нет. Я сам все еще использую телетайп для связи с мэйнфреймом, как я делал с тех пор, как начал работать в этой области. Хотя мои пальцы устают, это просто неправильно без грохота.