Да, команда Bash 'eval' может сделать эту работу. 'eval' не очень элегантен, и иногда бывает сложно понять и отладить код, который его использует. Я обычно стараюсь избегать этого, но Bash часто оставляет вас без другого выбора (как ситуация, которая вызвала ваш вопрос). Вам придется взвесить все за и против использования eval для себя.
Фон "eval"
Если вы не знакомы с 'eval', это встроенная команда Bash, которая ожидает, что вы передадите ей строку в качестве параметра. 'eval' динамически интерпретирует и выполняет вашу строку как команду самостоятельно, в текущем контексте оболочки и области видимости. Вот базовый пример общего использования (назначение динамических переменных):
$> a_var_name="color"
$> eval ${a_var_name}="blue"
$> echo -e "The color is ${color}."
The color is blue.
См. Руководство по расширенному написанию сценариев Bash для получения дополнительной информации и примеров: http://tldp.org/LDP/abs/html/internal.html#EVALREF
Решение вашей «исходной» проблемы
Чтобы 'eval' справился с вашей проблемой с источником, вы должны начать с переписывания вашей функции '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`
(Это обратные кавычки / обратные кавычки, кстати.)
Как это работает
Короче говоря:
- Мы преобразовали функцию в эмиттер командной строки.
- Наша новая функция генерирует строку вызова команды 'eval'.
- Наши новые метки обратного вызова вызывают новую функцию в контексте подоболочки, возвращая командную строку 'eval', возвращаемую функцией обратно в основной скрипт.
- Основной сценарий выполняет командную строку 'eval', захваченную обратными чертами, в контексте основного сценария.
- Командная строка 'eval' повторно анализирует и выполняет командную строку 'eval' в контексте основного сценария, выполняя весь блок if-then-else, включая (если файл существует) выполнение команды 'source'.
Это довольно сложно. Как я уже сказал, eval не совсем элегантен. В частности, есть несколько особых моментов, которые вы должны обратить внимание на сделанные нами изменения:
- Весь блок IF-THEN-ELSE превратился в одну целую строку в двойных кавычках с обратными слешами в конце каждой строки, скрывающими символы новой строки.
- Некоторые из специальных символов оболочки, такие как '"'), экранированы обратной косой чертой, а другие ('$') остались без экранирования.
- 'echo 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>
Удачи.