Я вже обговорював те, як і в чому спосіб, як описані нижче методи, кілька разів раніше, тому я більше не буду це робити. Особисто мої власні фаворити з цієї теми є тут і тут .
Якщо вам не цікаво читати це, але все ще цікаво, просто зрозумійте, що тут документи, приєднані до входу функції, оцінюються для розширення оболонки до запуску функції, і що вони генеруються заново в стані, в якому вони були, коли функція була визначена щоразу, коли функція викликається.
ДЕКЛАРАЦІЯ
Вам просто потрібна функція, яка оголошує інші функції.
_fn_init() { . /dev/fd/4 ; } 4<<INIT
${1}() { $(shift ; printf %s\\n "$@")
} 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 $(printf "'%s' " "$@")
RESET
INIT
РАБИТИ ЦЕ
Тут я закликаю _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
виклику, коли він не встановлений, але він ніколи не працює в першу чергу . Це найважливіший фактор, який потрібно зрозуміти при роботі з розширеннями тут-документами - вони завжди повинні виникати першими, оскільки вони є <<input
.
Помилка виникає /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<<\RESET
вхідному файлі-дескрипторі. Настільки .dot
джерело, що в оболонці в будь-який час повториться процес, який його встановив в першу чергу. Це все дуже просто, насправді і майже повністю портативний, якщо ви хочете не помітити той факт, що POSIX насправді не визначає шляхи до вузла файлового дескриптора пристрою (які є необхідними для оболонки .dot
).
Ви можете легко розширити цю поведінку і налаштувати різні стани для своєї функції.
БІЛЬШЕ?
Це ледь не дряпає поверхню, до речі. Я часто використовую ці методи для вбудовування маленьких допоміжних функцій, які можна виявити в будь-який момент, на вхід основної функції - наприклад, для додаткових позиційних $@
масивів за потребою. Насправді - як я вважаю, це має бути щось дуже близьке до цього, як це роблять снаряди вищого порядку. Ви можете бачити, що їх дуже легко запрограмувати програмно.
Мені також подобається оголосити функцію генератора, яка приймає обмежений тип параметрів, а потім визначає функцію пальника одноразового використання або іншим чином обмежена сферою дії в межах лямбда - або вбудованої функції - яка просто unset -f
є самою, коли наскрізь. Ви можете передавати функцію оболонки навколо.