удар: функционируйте +, источник + объявляет = бум

Вот проблема:

В моих сценариях удара я хочу получить несколько файлов с некоторыми проверками, таким образом, я имею:

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 сделал бы мою жизнь легче, но при работе с унаследованной системой, у каждого не всегда есть полномочие выбора лучшего инструмента.

7
задан Mogsdad 28 August 2015 в 02:51
поделиться

2 ответа

Да, команда 'eval' в Bash может заставить это работать. '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.

Более подробную информацию и примеры см. в Advanced Bash Scripting Guide: http://tldp.org/LDP/abs/html/internal.html#EVALREF

Решение проблемы с 'source'

Чтобы заставить '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' обратно в основной скрипт.
  • Основной сценарий выполняет командную строку '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>

Удачи.

8
ответ дан 6 December 2019 в 12:46
поделиться

declare внутри функции делает переменную локальной для этой функции. export влияет на окружение дочерних процессов, а не на текущее или родительское окружение.

Вы можете установить значения переменных внутри функций и сделать declare -r, declare -i или declare -ri уже после этого.

1
ответ дан 6 December 2019 в 12:46
поделиться
Другие вопросы по тегам:

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