J'ai déjà discuté du comment et du pourquoi du fonctionnement des méthodes ci-dessous à plusieurs reprises auparavant, donc je ne le referai pas. Personnellement, mes propres favoris sur le sujet sont ici et ici .
Si vous n'êtes pas intéressé à lire cela, mais toujours curieux, comprenez simplement que les documents ici attachés à l'entrée de la fonction sont évalués pour l'expansion du shell avant que la fonction ne s'exécute, et qu'ils sont générés à nouveau dans l'état où ils étaient lorsque la fonction a été définie chaque fois que la fonction est appelée.
DÉCLARER
Vous avez juste besoin d'une fonction qui déclare d'autres fonctions.
_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
EXÉCUTER
Ici, j'appelle _fn_init
à me déclarer une fonction appelée 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
OBLIGATOIRE
Si je veux appeler cette fonction, elle mourra à moins que la variable d'environnement ne _if_unset
soit définie.
fn
#OUTPUT#
+ fn
/dev/fd/4: line 1: _if_unset: shell will ERR and print this to stderr
Veuillez noter l'ordre des traces du shell - non seulement l' fn
échec lorsqu'il est appelé quand _if_unset
n'est pas défini, mais il ne s'exécute jamais en premier lieu . C'est le facteur le plus important à comprendre lorsque vous travaillez avec des extensions de document ici - elles doivent toujours se produire en premier car elles le sont <<input
après tout.
L'erreur provient du /dev/fd/4
fait que le shell parent évalue cette entrée avant de la transmettre à la fonction. C'est le moyen le plus simple et le plus efficace de tester l'environnement requis.
Quoi qu'il en soit, l'échec est facilement résolu.
_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
SOUPLE
La variable common_param
est évaluée à une valeur par défaut en entrée pour chaque fonction déclarée par _fn_init
. Mais cette valeur est également modifiable par toute autre qui sera également honorée par chaque fonction déclarée de la même manière. Je vais laisser les traces d'obus maintenant - nous n'entrerons dans aucun territoire inexploré ici ou quoi que ce soit.
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 ;
Ci-dessus, je déclare deux fonctions et définit _if_unset
. Maintenant, avant d'appeler l'une ou l'autre fonction, je vais la désélectionner common_param
pour que vous puissiez voir qu'ils la régleront eux-mêmes lorsque je les appellerai.
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
Et maintenant de la portée de l'appelant:
echo $common_param
#OUTPUT#
REQ/RESET added to all funcs
Mais maintenant, je veux que ce soit autre chose:
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.
Et si je ne bouge pas _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
RÉINITIALISER
Si vous devez réinitialiser l'état de la fonction à tout moment, cela se fait facilement. Il vous suffit de faire (depuis la fonction):
. /dev/fd/5
J'ai enregistré les arguments utilisés pour déclarer initialement la fonction dans le 5<<\RESET
descripteur de fichier d'entrée. Donc, l' .dot
approvisionnement dans le shell à tout moment répétera le processus qui l'a configuré en premier lieu. Tout cela est assez facile, vraiment et à peu près entièrement portable si vous êtes prêt à ignorer le fait que POSIX ne spécifie pas réellement les chemins de nœud du périphérique descripteur de fichier (qui sont une nécessité pour le shell .dot
).
Vous pouvez facilement développer ce comportement et configurer différents états pour votre fonction.
PLUS?
Soit dit en passant, cela raye à peine la surface. J'utilise souvent ces techniques pour incorporer de petites fonctions d'aide déclarables à tout moment dans l'entrée d'une fonction principale - par exemple, pour des $@
tableaux de position supplémentaires selon les besoins. En fait - comme je le crois, ce doit être quelque chose de très proche de cela que les obus d'ordre supérieur font de toute façon. Vous pouvez voir qu'ils sont très facilement nommés par programmation.
J'aime aussi déclarer une fonction de générateur qui accepte un type de paramètre limité et définit ensuite une fonction de brûleur à usage unique ou à portée limitée le long des lignes d'une lambda - ou d'une fonction en ligne - qui est tout simplement unset -f
elle-même lorsque à travers. Vous pouvez passer une fonction shell autour.
typeset
nécessaire? Ne le déclarerait-il pas autrement?