В python мы можем украшать функции кодом, который автоматически применяется и выполняется для функций.
Есть ли подобная особенность в bash?
В сценарии, над которым я сейчас работаю, у меня есть несколько шаблонов, которые проверяют необходимые аргументы и завершают работу, если они не существуют, и отображают некоторые сообщения, если установлен флаг отладки.
К сожалению, я должен повторно вставить этот код в каждую функцию, и если я хочу изменить его, мне придется изменить каждую функцию.
Есть ли способ удалить этот код из каждой функции и применить его ко всем функциям, аналогично декораторам в python?
Ответы:
Это было бы намного проще с
zsh
анонимными функциями и специальным ассоциативным массивом с кодами функций. Сbash
однако вы могли бы сделать что - то вроде:Что бы вывести:
Вы не можете вызвать украшать дважды, чтобы украсить вашу функцию дважды, хотя.
С
zsh
:источник
typeset
нужен? Не объявит ли это иначе?eval "_inner_$(typeset -f x)"
создает_inner_x
как точную копию оригиналаx
(так же, какfunctions[_inner_x]=$functions[x]
вzsh
).return
.Я уже несколько раз обсуждал, как и почему методы, описанные ниже, работают, поэтому я не буду делать это снова. Лично мои любимые по теме здесь и здесь .
Если вам не интересно читать это, но все же любопытно, просто поймите, что документы here, прикрепленные к входу функции, оцениваются на предмет расширения оболочки перед выполнением функции, и что они генерируются заново в том состоянии, в котором они были, когда функция была определена каждый раз, когда функция вызывается.
DECLARE
Вам просто нужна функция, которая объявляет другие функции.
ЗАПУСТИТЬ ЕГО
Здесь я призываю
_fn_init
объявить мне функцию с именемfn
.ТРЕБУЕТСЯ
Если я хочу вызвать эту функцию, она умрет, если не установлена переменная окружения
_if_unset
.Обратите внимание на порядок трассировки оболочки - не только происходит
fn
сбой при вызове, когда_if_unset
не установлен, но он никогда не запускается в первую очередь . Это самый важный фактор, который нужно понимать при работе с расширениями здесь-документа - они всегда должны появляться в первую очередь, потому что они есть в<<input
конце концов.Ошибка возникает из-
/dev/fd/4
за того, что родительская оболочка оценивает этот ввод перед передачей его функции. Это самый простой и самый эффективный способ проверки необходимой среды.Во всяком случае, сбой легко исправить.
ГИБКИЙ
Переменная
common_param
оценивается как значение по умолчанию на входе для каждой функции, объявленной_fn_init
. Но это значение также может быть изменено на любое другое, что также будет учитываться любой функцией, объявленной аналогичным образом. Я оставлю следы снарядов сейчас - мы не собираемся идти ни на какую неизведанную территорию или что-то еще.Выше я объявляю две функции и установить
_if_unset
. Теперь, прежде чем вызывать любую функцию, я отключу ее,common_param
чтобы вы могли видеть, что они сами установят ее при вызове.А теперь из области звонящего:
Но теперь я хочу, чтобы это было что-то совершенно другое:
А если я сбросил
_if_unset
?СБРОС
Если вам нужно сбросить состояние функции в любое время, это легко сделать. Вам нужно только сделать (изнутри функции):
Я сохранил аргументы, используемые для первоначального объявления функции во
5<<\RESET
входном файле-дескрипторе. Так.dot
что источник в оболочке в любое время повторит процесс, который его настроил в первую очередь. Это все довольно просто, на самом деле, и в значительной степени полностью переносимо, если вы не хотите учитывать тот факт, что POSIX фактически не указывает пути к узлам файлового дескриптора устройства (которые необходимы для оболочки.dot
).Вы можете легко расширить это поведение и настроить различные состояния для вашей функции.
БОЛЬШЕ?
Кстати, это едва царапает поверхность. Я часто использую эти методы для встраивания небольших вспомогательных функций, объявляемых в любое время на вход основной функции - например, для дополнительных позиционных
$@
массивов по мере необходимости. На самом деле - как я полагаю, это должно быть нечто очень близкое к тому, что делают оболочки высшего порядка в любом случае. Вы можете видеть, что они очень легко программно названы.Я также хотел бы объявить функцию генератора, которая принимает ограниченный тип параметра, а затем определяет одноразовую или иначе ограниченную областью функцию горелки вдоль линий лямбда-функции или встроенную функцию, которая просто
unset -f
сама по себе, когда через. Вы можете передать функцию оболочки вокруг.источник
eval
?.dot
работает с файлами и потоками, поэтому вы не столкнетесь с такими же проблемами списка аргументов, которые могли бы возникнуть в противном случае. Тем не менее, это, вероятно, вопрос предпочтений. Я, конечно, думаю, что это чище - особенно когда вы начинаете ценить Eval - это кошмар с того места, где я сижу..dot
пока вы не хорошо и не готовы - или когда-либо. Это дает вам немного больше свободы в тестировании своих оценок. И это обеспечивает гибкость состояния при вводе - что может быть обработано другими способами - но это гораздо менее опасно с этой точки зрения, чем естьeval
.Я думаю, что один из способов напечатать информацию о функции, когда вы
это изменение встроенной команды bash
return
и / илиexit
в начале каждого скрипта (или в каком-либо файле, который вы используете каждый раз перед выполнением программы). Итак, вы печатаетеЕсли вы запустите это, вы получите:
Это может быть легко обновлено с помощью флага отладки, если вам нужно, примерно так:
Этот способ будет выполняться только тогда, когда установлена переменная VERBOSE (по крайней мере, так я использую многословную в моих скриптах). Это, конечно, не решает проблему декорирования функции, но может отображать сообщения в случае, если функция возвращает ненулевой статус.
Точно так же вы можете переопределить
exit
, заменив все экземплярыreturn
, если вы хотите выйти из скрипта.РЕДАКТИРОВАТЬ: Я хотел бы добавить сюда, как я использую для украшения функций в Bash, если у меня их много, а также вложенных. Когда я пишу этот скрипт:
И для вывода я могу получить это:
Это может быть полезно для тех, кто имеет функции и хочет их отладить, чтобы увидеть, в какой функции произошла ошибка. Он основан на трех функциях, которые могут быть описаны ниже:
Я пытался поставить как можно больше в комментариях, но здесь также описание: Я использую
_ ()
функцию декоратора, один я помещал после объявления каждой функции:foo () { _
. Эта функция печатает имя функции с правильным отступом, в зависимости от глубины функции в другой функции (в качестве отступа по умолчанию я использую 4 числа пробелов). Я обычно печатаю это серым цветом, чтобы отделить это от обычной печати. Если необходимо декорировать функцию с аргументами или без нее, можно изменить пред-последнюю строку в функции декоратора.Чтобы напечатать что-то внутри функции, я ввел
print ()
функцию, которая печатает все, что ему передано, с соответствующим отступом.Функция
set_indentation_for_print_function
делает именно то, что она обозначает, вычисляя отступы из${FUNCNAME[@]}
массива.У этого способа есть некоторые недостатки, например, нельзя передать опции , например ,
print
like или , а также, если функция возвращает 1, она не оформлена. А также для аргументов, передаваемых на ширину терминала, которые будут перенесены на экран, отступ для обернутой строки не будет виден.echo
-n
-e
print
Отличный способ использовать эти декораторы - поместить их в отдельный файл и в каждом новом скрипте получить этот файл
source ~/script/hand_made_bash_functions.sh
.Я думаю, что лучший способ включить декоратор функций в bash, это написать декоратор в теле каждой функции. Я думаю, что намного проще написать функцию внутри функции в bash, потому что она имеет возможность устанавливать все переменные глобально, а не как в стандартных объектно-ориентированных языках. Это делает так, как будто вы размещаете метки вокруг кода в bash. По крайней мере, это помогло мне для отладки скриптов.
источник
Возможно, вам помогут примеры декораторов в проекте http://sourceforge.net/projects/oobash/ (oobash / docs / examples / decorator.sh).
источник
Для меня это похоже на самый простой способ реализовать шаблон декоратора внутри bash.
источник
"$@"
).Я много (возможно, слишком много) метапрограммирую в Bash, и нашел декораторы неоценимыми для повторной реализации поведения на лету. Моя библиотека bash-cache использует декорацию для прозрачного запоминания функций Bash с минимальной церемонией:
Очевидно, что
bc::cache
это больше, чем просто оформление, но основное оформление полагается на то,bc::copy_function
чтобы скопировать существующую функцию в новое имя, чтобы исходная функция могла быть перезаписана декоратором.Вот простой пример декоратора, который
time
выполняет функцию декорирования, используяbc::copy_function
:Демо-версия:
источник