En python podemos decorar funciones con código que se aplica y ejecuta automáticamente contra funciones.
¿Hay alguna característica similar en bash?
En la secuencia de comandos en la que estoy trabajando actualmente, tengo algunas repeticiones que prueban los argumentos necesarios y salen si no existen, y muestran algunos mensajes si se especifica el indicador de depuración.
Desafortunadamente, tengo que reinsertar este código en cada función y si quiero cambiarlo, tendré que modificar cada función.
¿Hay alguna forma de eliminar este código de cada función y aplicarlo a todas las funciones, de forma similar a los decoradores en python?
typeset
necesario? ¿No lo declararía de otra manera?eval "_inner_$(typeset -f x)"
crea_inner_x
como una copia exacta del originalx
(igual quefunctions[_inner_x]=$functions[x]
enzsh
).return
.Ya he discutido los cómo y los porqués de la forma en que funcionan los métodos a continuación en varias ocasiones antes, así que no lo volveré a hacer. Personalmente, mis favoritos sobre el tema están aquí y aquí .
Si no está interesado en leer eso, pero todavía tiene curiosidad, comprenda que los documentos adjuntos a la entrada de la función se evalúan para la expansión del shell antes de que se ejecute la función, y que se generan nuevamente en el estado en que estaban cuando se definió la función cada vez que se llama a la función.
DECLARAR
Solo necesita una función que declare otras funciones.
EJECUTARLO
Aquí llamo
_fn_init
para declararme una función llamadafn
.NECESARIO
Si quiero llamar a esta función, morirá a menos que se establezca la variable de entorno
_if_unset
.Tenga en cuenta el orden de los rastreos de shell: no solo
fn
falla cuando se llama cuando no_if_unset
está configurado, sino que nunca se ejecuta en primer lugar . Este es el factor más importante para entender cuando se trabaja con expansiones de documentos aquí: siempre deben ocurrir primero porque<<input
después de todo lo son .El error proviene de
/dev/fd/4
que el shell principal está evaluando esa entrada antes de pasarla a la función. Es la forma más simple y eficiente de probar el entorno requerido.De todos modos, la falla se remedia fácilmente.
FLEXIBLE
La variable
common_param
se evalúa a un valor predeterminado en la entrada para cada función declarada por_fn_init
. Pero ese valor también se puede cambiar a cualquier otro que también sea honrado por cada función declarada de manera similar. Ahora dejaré los rastros de la cáscara: no vamos a entrar en ningún territorio desconocido aquí ni nada.Arriba declaro dos funciones y conjunto
_if_unset
. Ahora, antes de llamar a cualquiera de las funciones, lo desarmarécommon_param
para que pueda ver que lo configurarán ellos mismos cuando los llame.Y ahora desde el alcance de la persona que llama:
Pero ahora quiero que sea algo completamente distinto:
¿Y si me desarmo
_if_unset
?REINICIAR
Si necesita restablecer el estado de la función en cualquier momento, puede hacerlo fácilmente. Solo necesita hacer (desde dentro de la función):
Guardé los argumentos utilizados para declarar inicialmente la función en el
5<<\RESET
descriptor de archivo de entrada. Por lo tanto, la.dot
fuente en el shell en cualquier momento repetirá el proceso que lo configuró en primer lugar. Todo es bastante fácil, realmente y prácticamente totalmente portátil si está dispuesto a pasar por alto el hecho de que POSIX no especifica realmente las rutas de nodo del dispositivo descriptor de archivos (que son una necesidad para el shell.dot
).Podría expandir fácilmente este comportamiento y configurar diferentes estados para su función.
¿MÁS?
Esto apenas rasca la superficie, por cierto. A menudo uso estas técnicas para incrustar pequeñas funciones auxiliares declarables en cualquier momento en la entrada de una función principal, por ejemplo, para
$@
matrices posicionales adicionales según sea necesario. De hecho, como creo, debe ser algo muy parecido a esto que los proyectiles de orden superior hacen de todos modos. Puedes ver que son muy fáciles de nombrar mediante programación.También me gusta declarar una función generadora que acepte un tipo limitado de parámetro y luego defina una función de quemador de un solo uso o limitada por el alcance a lo largo de las líneas de una lambda, o una función en línea, que simplemente
unset -f
es en sí misma cuando mediante. Puede pasar una función de shell.fuente
eval
?.dot
funciona con archivos y secuencias para que no se encuentre con el mismo tipo de problemas de listas de argumentos que de otro modo podría tener. Aún así, es probablemente una cuestión de preferencia. Ciertamente creo que es más limpio, especialmente cuando te pones a evaluar eval, es una pesadilla desde donde estoy sentado..dot
hasta que esté bien y listo, o nunca. Esto le permite un poco más de libertad para probar sus evaluaciones. Y proporciona la flexibilidad del estado en la entrada, que se puede manejar de otras maneras, pero es mucho menos peligroso desde esa perspectiva de lo que eseval
.Creo que una forma de imprimir información sobre la función, cuando
es cambiar bash incorporado
return
y / oexit
al comienzo de cada script (o en algún archivo, que obtiene cada vez antes de ejecutar el programa). Entonces escribesSi ejecuta esto obtendrá:
Eso puede actualizarse fácilmente con el indicador de depuración si lo necesita, algo así:
De esta manera, la declaración se ejecutará solo cuando se establezca la variable VERBOSE (al menos así es como uso verbose en mis scripts). Ciertamente no resuelve el problema de la función de decoración, pero puede mostrar mensajes en caso de que la función devuelva un estado distinto de cero.
Del mismo modo, puede redefinir
exit
, reemplazando todas las instancias dereturn
, si desea salir del script.EDITAR: quería agregar aquí la forma en que uso para decorar funciones en bash, si tengo muchas de ellas y también anidadas. Cuando escribo este script:
Y para la salida puedo obtener esto:
Puede ser útil para alguien que tiene funciones y quiere depurarlas, para ver en qué función se produjo el error. Se basa en tres funciones, que pueden describirse a continuación:
Traté de poner lo más posible en los comentarios, pero aquí está también la descripción: Consumo
_ ()
funcionan como decorador, la puse después de la declaración de cada función:foo () { _
. Esta función imprime el nombre de la función con la sangría adecuada, dependiendo de qué tan profunda sea la función en otra función (como sangría predeterminada utilizo 4 números de espacios). Normalmente imprimo esto en gris, para separar esto de la impresión habitual. Si se necesita decorar la función con argumentos, o sin ella, se puede modificar la última línea en la función decoradora.Para imprimir algo dentro de la función, introduje una
print ()
función que imprime todo lo que se le pasa con la sangría adecuada.La función
set_indentation_for_print_function
hace exactamente lo que representa, calculando la sangría de la${FUNCNAME[@]}
matriz.De esta manera tiene algunos defectos, por ejemplo, uno no puede pasar las opciones a
print
gustarecho
, por ejemplo ,-n
o-e
, y también si la función devuelve 1, no está decorado. Y también para argumentos, pasados aprint
más del ancho del terminal, que se ajustarán en la pantalla, uno no verá la sangría para la línea ajustada.La mejor manera de usar estos decoradores es colocarlos en un archivo separado y en cada nuevo script para obtener este archivo
source ~/script/hand_made_bash_functions.sh
.Creo que la mejor manera de incorporar el decorador de funciones en bash es escribir el decorador en el cuerpo de cada función. Creo que es mucho más fácil escribir la función dentro de la función en bash, porque tiene la opción de establecer todas las variables globales, no como en los lenguajes orientados a objetos estándar. Eso hace que parezca que estás poniendo etiquetas alrededor de tu código en bash. Al menos eso me ayudó para los scripts de depuración.
fuente
Tal vez los ejemplos de decorador en http://sourceforge.net/projects/oobash/ project pueden ayudarlo (oobash / docs / examples / decorator.sh).
fuente
Para mí, esto se siente como la forma más sencilla de implementar un patrón de decorador dentro de bash.
fuente