Я уже несколько раз уже обсуждал, как и почему работают указанные ниже методы, поэтому я не буду повторять это снова. Лично мои фавориты по этой теме - здесь и здесь .
Если вам не интересно это читать, но все же любопытно, просто поймите, что здесь-документы, прикрепленные к входу функции, оцениваются для расширения оболочки до выполнения функции, и что они создаются заново в состояние, в котором они были, когда функция была определена каждый раз, когда функция вызывается.
Вам просто нужна функция, которая объявляет другие функции.
_fn_init() { . /dev/fd/4 ; } 4<
Здесь я призываю _fn_init
объявить мне функцию с именем fn
.
set -vx
_fn_init fn \
'echo "this would be command 1"' \
'echo "$common_param"'
#OUTPUT#
+ _fn_init fn 'echo "this would be command 1"' 'echo "$common_param"'
shift ; printf %s\\n "$@"
++ shift
++ printf '%s\n' 'echo "this would be command 1"' 'echo "$common_param"'
printf "'%s' " "$@"
++ printf ''\''%s'\'' ' fn 'echo "this would be command 1"' 'echo "$common_param"'
#ALL OF THE ABOVE OCCURS BEFORE _fn_init RUNS#
#FIRST AND ONLY COMMAND ACTUALLY IN FUNCTION BODY BELOW#
+ . /dev/fd/4
#fn AFTER _fn_init .dot SOURCES IT#
fn() { echo "this would be command 1"
echo "$common_param"
} 4<<-REQ 5<<-\RESET
: ${_if_unset?shell will ERR and print this to stderr}
: ${common_param="REQ/RESET added to all funcs"}
REQ
_fn_init 'fn' \
'echo "this would be command 1"' \
'echo "$common_param"'
RESET
Если я захочу вызвать эту функцию, она умрет, если не будет установлена переменная окружения _if_unset
.
fn
#OUTPUT#
+ fn
/dev/fd/4: line 1: _if_unset: shell will ERR and print this to stderr
Обратите внимание на порядок трассировки оболочки - не только fn
не работает при вызове, когда _if_unset
не задано, но никогда не запускается ]. Это самый важный фактор, который следует понимать при работе с расширениями здесь-документа - они всегда должны появляться первыми, потому что в конце концов они .
Ошибка возникает из / dev / fd / 4
, потому что родительская оболочка оценивает этот ввод перед передачей его функции. Это самый простой и эффективный способ проверить необходимую среду.
Так или иначе, неисправность легко исправить.
_if_unset=set fn
#OUTPUT#
+ _if_unset=set
+ fn
+ echo 'this would be command 1'
this would be command 1
+ echo 'REQ/RESET added to all funcs'
REQ/RESET added to all funcs
Переменная common_param
оценивается как значение по умолчанию на входе для каждой функции, объявленной _fn_init
. Но это значение также можно изменить на любое другое, которое также будет соблюдаться каждой аналогичной объявленной функцией. Я не буду сейчас останавливаться на следах снарядов - мы не собираемся здесь ни на какие неизведанные территории или что-то в этом роде.
set +vx
_fn_init 'fn' \
'echo "Hi! I am the first function."' \
'echo "$common_param"'
_fn_init 'fn2' \
'echo "This is another function."' \
'echo "$common_param"'
_if_unset=set ;
Выше я объявляю две функции и устанавливаю _if_unset
. Теперь, перед вызовом любой из функций, я отключу common_param
, чтобы вы могли видеть, что они устанавливают его сами, когда я их вызываю.
unset common_param ; echo
fn ; echo
fn2 ; echo
#OUTPUT#
Hi! I am the first function.
REQ/RESET added to all funcs
This is another function.
REQ/RESET added to all funcs
А теперь из области действия вызывающего:
echo $common_param
#OUTPUT#
REQ/RESET added to all funcs
Но теперь я хочу, чтобы это было совсем другое:
common_param="Our common parameter is now something else entirely."
fn ; echo
fn2 ; echo
#OUTPUT#
Hi! I am the first function.
Our common parameter is now something else entirely.
This is another function.
Our common parameter is now something else entirely.
И если я отключу _if_unset
?
unset _if_unset ; echo
echo "fn:"
fn ; echo
echo "fn2:"
fn2 ; echo
#OUTPUT#
fn:
dash: 1: _if_unset: shell will ERR and print this to stderr
fn2:
dash: 1: _if_unset: shell will ERR and print this to stderr
Если вам нужно сбросить состояние функции в любой момент легко.Вам нужно только сделать (изнутри функции):
. /dev/fd/5
Я сохранил аргументы, использованные для первоначального объявления функции, во входном файловом дескрипторе 5 . Таким образом, поиск
.dot
, который находится в оболочке, в любой момент повторит процесс, который изначально установил его. На самом деле все довольно просто и практически полностью переносимо, если вы хотите упустить из виду тот факт, что POSIX на самом деле не указывает пути к узлам файлового дескриптора устройства (которые необходимы для .dot
оболочки ]).
Вы можете легко расширить это поведение и настроить различные состояния для своей функции.
Это, кстати, почти не царапает поверхность. Я часто использую эти методы для встраивания небольших вспомогательных функций, объявляемых в любое время, во входные данные основной функции - например, для дополнительных позиционных массивов $ @
по мере необходимости. Фактически - как я считаю, это должно быть что-то очень близкое к этому, что в любом случае делают оболочки более высокого порядка. Вы можете видеть, что им очень легко присваивать программные названия.
Мне также нравится объявлять функцию генератора, которая принимает параметр ограниченного типа, а затем определяет одноразовую или иным образом ограниченную по объему функцию записи в виде лямбда - или встроенной функции - которая просто unset -f
сам по себе. Вы можете передать функцию оболочки.
Если цель состоит в том, чтобы просто запустить каждую функцию и избавиться от каких-либо ошибок, возможно, что-то вроде этого сработает:
function bail {
echo "se ha producido un error ($1)"
exit 1
}
function a {
uptime
}
function b {
cat /etc/redhat-release
}
function c {
cat /etc/redhat-release
}
for f in 'a' 'b' 'c'; do
$f || bail "$f: $?"
done
Это запустит каждую функцию, и в случае возникновения ошибки имя функции и код выхода будут отправлены в функцию bail
, которая напечатает строку и отключит любые дополнительные команды.
Я удалил использование su
, потому что так было проще тестировать.Если вы хотите автоматизировать это, я бы порекомендовал создать профиль sudo для запуска этих команд с повышенными привилегиями таким образом, чтобы вам нужно было вводить пароль только один раз или вообще ни разу.
debug=$?
оценивается не при каждой команде. Таким образом, значение debug
будет одинаковым для всего скрипта. Таким образом, что-то вроде a && b && c
запустит b
, если a
завершится успешно, и запустит c
, если b
выполнится успешно. Код возврата этой составной команды является первым ненулевым -кодом возврата каждой из функций.