En python, nous pouvons décorer des fonctions avec du code qui est automatiquement appliqué et exécuté par rapport aux fonctions.
Existe-t-il une fonctionnalité similaire dans bash?
Dans le script sur lequel je travaille actuellement, j'ai un passe-partout qui teste les arguments requis et quitte s'ils n'existent pas - et affiche certains messages si l'indicateur de débogage est spécifié.
Malheureusement, je dois réinsérer ce code dans chaque fonction et si je veux le changer, je devrai modifier chaque fonction.
Existe-t-il un moyen de supprimer ce code de chaque fonction et de l'appliquer à toutes les fonctions, comme les décorateurs en python?
typeset
nécessaire? Ne le déclarerait-il pas autrement?eval "_inner_$(typeset -f x)"
crée_inner_x
une copie exacte de l'originalx
(identiquefunctions[_inner_x]=$functions[x]
àzsh
).return
.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.
EXÉCUTER
Ici, j'appelle
_fn_init
à me déclarer une fonction appeléefn
.OBLIGATOIRE
Si je veux appeler cette fonction, elle mourra à moins que la variable d'environnement ne
_if_unset
soit définie.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.
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.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électionnercommon_param
pour que vous puissiez voir qu'ils la régleront eux-mêmes lorsque je les appellerai.Et maintenant de la portée de l'appelant:
Mais maintenant, je veux que ce soit autre chose:
Et si je ne bouge pas
_if_unset
?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):
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.la source
eval
?.dot
fonctionne avec des fichiers et des flux afin que vous ne rencontriez pas le même type de problèmes de liste d'arguments que vous pourriez autrement. Pourtant, c'est probablement une question de préférence. Je pense certainement que c'est plus propre - surtout quand vous vous évaluez à évaluer - c'est un cauchemar d'où je m'assois..dot
vous approvisionner tant que vous n'êtes pas bon et prêt - ou jamais. Cela vous donne un peu plus de liberté pour tester ses évaluations. Et il offre la flexibilité de l'état sur l'entrée - qui peut être gérée autrement - mais c'est beaucoup moins dangereux de ce point de vue que ce n'est le caseval
.Je pense qu'une façon d'imprimer des informations sur la fonction, lorsque vous
est de changer le bash intégré
return
et / ouexit
au début de chaque script (ou dans un fichier, que vous sourcez à chaque fois avant d'exécuter le programme). Alors vous tapezSi vous exécutez cela, vous obtiendrez:
Cela peut être facilement mis à jour avec l'indicateur de débogage si vous en avez besoin, un peu comme ceci:
Cette instruction way ne sera exécutée que lorsque la variable VERBOSE est définie (du moins c'est ainsi que j'utilise verbose dans mes scripts). Cela ne résout certainement pas le problème de la fonction de décoration, mais il peut afficher des messages au cas où la fonction retourne un état non nul.
De même, vous pouvez redéfinir
exit
, en remplaçant toutes les instances dereturn
, si vous souhaitez quitter le script.EDIT: Je voulais ajouter ici la façon dont j'utilise pour décorer les fonctions en bash, si j'en ai beaucoup et des imbriquées également. Quand j'écris ce script:
Et pour la sortie, je peux obtenir ceci:
Il peut être utile à quelqu'un qui a des fonctions et souhaite les déboguer, de voir dans quelle erreur de fonction s'est produite. Il est basé sur trois fonctions, qui peuvent être décrites ci-dessous:
J'ai essayé de mettre autant que possible dans les commentaires, mais voici aussi la description: J'utilise la
_ ()
fonction comme décorateur, celui que je mets après la déclaration de toutes les fonctions:foo () { _
. Cette fonction imprime le nom de la fonction avec l'indentation appropriée, en fonction de la profondeur de la fonction dans une autre fonction (en tant qu'indentation par défaut, j'utilise 4 nombres d'espaces). J'imprime généralement ceci en gris, pour le séparer de l'impression habituelle. Si la fonction doit être décorée avec ou sans arguments, on peut modifier l'avant-dernière ligne dans la fonction décorateur.Afin d'imprimer quelque chose à l'intérieur de la fonction, j'ai introduit une
print ()
fonction qui imprime tout ce qui lui est transmis avec un retrait correct.La fonction
set_indentation_for_print_function
fait exactement ce qu'elle représente, calculant l'indentation à partir du${FUNCNAME[@]}
tableau.Cette façon a quelques défauts, par exemple on ne peut pas passer d'options à
print
aimerecho
, par exemple-n
ou-e
, et aussi si la fonction retourne 1, elle n'est pas décorée. Et aussi pour les arguments, passés àprint
plus de la largeur du terminal, qui seront enveloppés à l'écran, on ne verra pas le retrait pour la ligne enveloppée.La meilleure façon d'utiliser ces décorateurs est de les mettre dans un fichier séparé et dans chaque nouveau script pour source ce fichier
source ~/script/hand_made_bash_functions.sh
.Je pense que la meilleure façon d'incorporer le décorateur de fonctions dans bash est d'écrire le décorateur dans le corps de chaque fonction. Je pense qu'il est beaucoup plus facile d'écrire une fonction à l'intérieur d'une fonction dans bash, car il a la possibilité de définir toutes les variables de manière globale, pas comme dans les langages orientés objet standard. Cela donne l'impression que vous mettez des étiquettes autour de votre code dans bash. Au moins, cela m'a aidé pour un débogage de scripts.
la source
Peut-être que les exemples de décorateur du projet http://sourceforge.net/projects/oobash/ peuvent vous aider (oobash / docs / examples / decorator.sh).
la source
Pour moi, cela semble être le moyen le plus simple d'implémenter un motif de décoration à l'intérieur de bash.
la source
"$@"
).