Как отладить сценарий, удалив «если»?

Я уже несколько раз уже обсуждал, как и почему работают указанные ниже методы, поэтому я не буду повторять это снова. Лично мои фавориты по этой теме - здесь и здесь .

Если вам не интересно это читать, но все же любопытно, просто поймите, что здесь-документы, прикрепленные к входу функции, оцениваются для расширения оболочки до выполнения функции, и что они создаются заново в состояние, в котором они были, когда функция была определена каждый раз, когда функция вызывается.

DECLARE

Вам просто нужна функция, которая объявляет другие функции.

_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

RESET

Если вам нужно сбросить состояние функции в любой момент легко.Вам нужно только сделать (изнутри функции):

. /dev/fd/5

Я сохранил аргументы, использованные для первоначального объявления функции, во входном файловом дескрипторе 5 . Таким образом, поиск .dot , который находится в оболочке, в любой момент повторит процесс, который изначально установил его. На самом деле все довольно просто и практически полностью переносимо, если вы хотите упустить из виду тот факт, что POSIX на самом деле не указывает пути к узлам файлового дескриптора устройства (которые необходимы для .dot оболочки ]).

Вы можете легко расширить это поведение и настроить различные состояния для своей функции.

БОЛЬШЕ?

Это, кстати, почти не царапает поверхность. Я часто использую эти методы для встраивания небольших вспомогательных функций, объявляемых в любое время, во входные данные основной функции - например, для дополнительных позиционных массивов $ @ по мере необходимости. Фактически - как я считаю, это должно быть что-то очень близкое к этому, что в любом случае делают оболочки более высокого порядка. Вы можете видеть, что им очень легко присваивать программные названия.

Мне также нравится объявлять функцию генератора, которая принимает параметр ограниченного типа, а затем определяет одноразовую или иным образом ограниченную по объему функцию записи в виде лямбда - или встроенной функции - которая просто unset -f сам по себе. Вы можете передать функцию оболочки.

0
06.02.2019, 22:02
2 ответа

Если цель состоит в том, чтобы просто запустить каждую функцию и избавиться от каких-либо ошибок, возможно, что-то вроде этого сработает:

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

0
28.01.2020, 04:01
  1. debug=$?оценивается не при каждой команде. Таким образом, значение debugбудет одинаковым для всего скрипта.
  2. Код возврата функции — это код возврата ее последней команды. Таким образом, операторы return бессмысленны.

Таким образом, что-то вроде a && b && cзапустит b, если aзавершится успешно, и запустит c, если bвыполнится успешно. Код возврата этой составной команды является первым ненулевым -кодом возврата каждой из функций.

0
28.01.2020, 04:01

Теги

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