Вот проблема:
В моих сценариях удара я хочу получить несколько файлов с некоторыми проверками, таким образом, я имею:
if [ -r foo ] ; then
source foo
else
logger -t $0 -p crit "unable to source foo"
exit 1
fi
if [ -r bar ] ; then
source bar
else
logger -t $0 -p crit "unable to source bar"
exit 1
fi
# ... etc ...
Наивно я пытался создать функцию, которые делают:
function safe_source() {
if [ -r $1 ] ; then
source $1
else
logger -t $0 -p crit "unable to source $1"
exit 1
fi
}
safe_source foo
safe_source bar
# ... etc ...
Но там существует препятствие.
Если один из файлов foo
, bar
, и т.д. имейте глобальное такой как-
declare GLOBAL_VAR=42
- это эффективно станет:
function safe_source() {
# ...
declare GLOBAL_VAR=42
# ...
}
таким образом глобальная переменная становится локальной.
Вопрос:
Псевдоним в ударе кажется слишком слабым для этого, таким образом, я должен развернуть вышеупомянутую функцию, и повторить меня или являюсь там более изящным подходом?
... и да, я соглашаюсь, что Python, Perl, Ruby сделал бы мою жизнь легче, но при работе с унаследованной системой, у каждого не всегда есть полномочие выбора лучшего инструмента.
Да, команда 'eval' в Bash может заставить это работать. 'eval' не очень элегантна, и иногда бывает трудно понять и отладить код, который ее использует. Обычно я стараюсь избегать ее, но Bash часто не оставляет другого выбора (как в ситуации, вызвавшей ваш вопрос). Вам придется самостоятельно взвесить все плюсы и минусы использования 'eval'.
Если вы не знакомы с 'eval', это встроенная команда Bash, которая ожидает, что вы передадите ей строку в качестве параметра. 'eval' динамически интерпретирует и выполняет вашу строку как самостоятельную команду в текущем контексте и области видимости оболочки. Вот базовый пример общего использования (динамическое присвоение переменных):
$> a_var_name="color"
$> eval ${a_var_name}="blue"
$> echo -e "The color is ${color}."
The color is blue.
Более подробную информацию и примеры см. в Advanced Bash Scripting Guide: http://tldp.org/LDP/abs/html/internal.html#EVALREF
Чтобы заставить 'eval' справиться с проблемой sourcing, начните с переписывания функции 'safe_source()'. Вместо фактического выполнения команды, 'safe_source()' должна просто распечатать команду в виде строки на STDOUT:
function safe_source() { echo eval " \
if [ -r $1 ] ; then \
source $1 ; \
else \
logger -t $0 -p crit \"unable to source $1\" ; \
exit 1 ; \
fi \
"; }
Также вам нужно немного изменить вызовы ваших функций, чтобы они действительно выполняли команду 'eval':
`safe_source foo`
`safe_source bar`
(Это обратные кавычки, BTW.)
Вкратце:
Это довольно сложно. Как я уже сказал, 'eval' не совсем элегантен. В частности, есть пара особых вещей, которые вы должны заметить в сделанных нами изменениях:
Большинство этих изменений вызвано тем, что 'eval' не может обрабатывать новые строки. Он может работать с несколькими командами, только если мы объединим их в одну строку, разделенную точками с запятой. Разрывы строк в новой функции - это просто удобство форматирования для человеческого глаза.
Если что-то из этого непонятно, запустите свой сценарий с включенным флагом Bash '-x' (отладка выполнения), и это должно дать вам лучшее представление о том, что именно происходит. Например, в контексте функции, функция фактически производит строку команды 'eval', выполняя эту команду:
echo eval ' if [ -r <INCL_FILE> ] ; then source <INCL_FILE> ; else logger -t <SCRIPT_NAME> -p crit "unable to source <INCL_FILE>" ; exit 1 ; fi '
Затем, в основном контексте, основной сценарий выполняет следующее:
eval if '[' -r <INCL_FILE> ']' ';' then source <INCL_FILE> ';' else logger -t <SCRIPT_NAME> -p crit '"unable' to source '<INCL_FILE>"' ';' exit 1 ';' fi
Наконец, опять же в основном контексте, команда eval выполняет эти две команды, если они существуют:
'[' -r <INCL_FILE> ']'
source <INCL_FILE>
Удачи.
declare
внутри функции делает переменную локальной для этой функции. export
влияет на окружение дочерних процессов, а не на текущее или родительское окружение.
Вы можете установить значения переменных внутри функций и сделать declare -r
, declare -i
или declare -ri
уже после этого.