Pasar un bloque de código como un anon. función


9

¿Es posible tratar un bloque de comandos como una función anónima?

function wrap_this {
   run_something
   # Decide to run block or maybe not.
   run_something else
}

wrap_this {
   do_something
   do_somthing else
}

# Do something else

wrap_this {
   do_something_else_else
   do_something_else_else_else
}

(Me doy cuenta de que crea una función o un archivo para cada bloque, pero creo que esta opción es más clara y fácil de leer en ciertas situaciones).

whilelo hace con do/doney functionlo hace con { multiple lines }. Me doy cuenta de que BASH no tiene funciones anónimas, pero ¿es posible pasar múltiples comandos a otra función, como puede hacer al definir una función o while?


¿Quiere decir que desea decorar (en lenguaje Python), es decir, devolver una función de una función? Su ejemplo, sintácticamente, ni siquiera es BASH: ¿se supone que wrap_this es una función o una llamada a función?
Mel Boyce

No me queda claro lo que quieres hacer. Como Mel señaló, lo que ha escrito incluso es sintácticamente válido, pero no me queda claro cómo se relaciona lo que ha escrito con las funciones anónimas.
Chris Down

Respuestas:


2

Esta es la solución más corta que se me ocurre:

Dadas estas funciones:

# List processing
map() { while IFS='' read -r x; do "$@" "$x"; done; }
filter() { while IFS='' read -r x; do "$@" "$x" >&2 && echo "$x"; done; }
foldr() { local f="$1"; local result="$2"; shift 2;  while IFS='' read -r x; do result="$( "$f" "$@" "$x" "$result" )"; done; echo "$result"; }
foldl() { local f="$1"; local result="$2"; shift 2;  while IFS='' read -r x; do result="$( "$f" "$@" "$result" "$x" )"; done; echo "$result"; }

# Helpers
re() { [[ "$2" =~ $1 ]]; }

Ejemplos:

# Example helpers
toLower() { tr '[:upper:]' '[:lower:]'; }
showStructure() { [[ "$1" == "--curly" ]] && echo "{$2; $3}" || echo "($1, $2)"; }

# All lib* directories, ignoring case, using regex
ls /usr | map toLower | filter re 'lib.*'

# All block devices. (Using test, for lack of a full bash [[ … ]].)
cd /dev; ls | filter test -b

# Show difference between foldr and foldl
$ ls / | foldr showStructure '()'
(var/, (usr/, (tmp/, (sys/, (sbin/, (run/, (root/, (proc/, (opt/, (mnt/, (media/, (lost+found/, (lib64/, (lib32/, (lib@, (home/, (etc/, (dev/, (daten/, (boot/, (bin/, ())))))))))))))))))))))
$ ls / | foldr showStructure '{}' --curly
{var/; {usr/; {tmp/; {sys/; {sbin/; {run/; {root/; {proc/; {opt/; {mnt/; {media/; {lost+found/; {lib64/; {lib32/; {lib@; {home/; {etc/; {dev/; {daten/; {boot/; {bin/; {}}}}}}}}}}}}}}}}}}}}}}

(Estos ejemplos son, por supuesto, solo ejemplos de uso, y en realidad, este estilo solo tendría sentido para casos de uso más complicados).

En general, siempre se puede usar el siguiente estilo:

f() { something "$@"       ; }; someList    | map    f
g() { something "$1" "$2" …; }; someCommand | filter g
                                               

No es del todo lambda, pero está muy, muy cerca. Solo unos pocos caracteres en exceso.

Pero cualquiera de las siguientes abreviaturas de conveniencia no funciona hasta donde puedo decir:

λ() { [[ $@ ]]; } # fails on spaces
λ() { [[ "${@:-1}" ${@:1:-1} ]]; } # syntax error
alias λ=test # somehow ignored

Desafortunadamente, bashno es muy adecuado para este estilo, aunque algunas de sus características tienen un estilo muy funcional.


¿Las características de bash tienen un estilo funcional? Lo único remotamente funcional es que bash (como casi cualquier lenguaje de shell) admite una forma de composición de funciones canalizando la salida de un comando a la entrada de otro. Sin embargo, esa es más una característica del diseño general de Unix, no bash per se.
kyrill

4

Me las arreglé para hacer lo que quieres con un evaltruco. Con esto advertí que eval es inseguro y que debes evitarlo a toda costa . Dicho esto, si confía en que no se abusará de su código, puede usar esto:

wrap_this(){
    run_something
    eval "$(cat /dev/stdin)"
    run_something_else
}

Esto le permite ejecutar el código de la siguiente manera:

wrap_this << EOF
    my function
EOF

No es exactamente ideal, ya que el bloque interno es una cadena, pero crea reutilización.


¡Esto es asombroso! Simplemente agregaría comillas alrededor del primero !!para evitar la sustitución en heredoc. stackoverflow.com/questions/27920806/…
Saintali

3

Se puede poner el código en una cadena y pasarlo a evalo sho simplemente interpolar ella.

perform () {
  "$@"
}

perform echo "moo"

Sin embargo, puede terminar rápidamente en problemas de citas.


1
En realidad, lo estaba buscando perform { echo "moo" \n echo "moo moo" \n echo "moo moo moo" }. Ya sabía que puedes pasar un comando. Pero, estoy buscando varias líneas, no solo un comando o una línea. Gracias por intentarlo.
dgo.a

3

No, bash no tiene funciones anónimas. Sin embargo, es posible pasar un nombre de función y argumentos como cadenas y hacer que bash lo llame.

function wrap() {
    do_before
    "$@"
    do_after
}

wrap do_something with_arguments

Sin embargo, esto es algo limitado. Tratar de citar puede convertirse en un problema. Pasar más de un comando también es una complicación.


1
No es un nombre nocturno, todo lo que tienes que hacer es cambiar $*a"$@"
glenn jackman

Los cuerpos de función multilínea deberían funcionar bien con este método.

Glenn, me olvidé de esa forma, gracias. Sin embargo, no resuelve completamente el problema de las citas. Evan, un solo comando envuelto en varias líneas funcionaría bien, me refería a tener más de un comando en el cuerpo según los ejemplos en la pregunta. Actualizaré mi respuesta para abordar ambos comentarios.
David Baggerman

Este formulario funciona muy bien si se ocupa de definir funciones que toman argumentos simples y luego pasa llamadas a esas funciones como argumentos. Es decir wrap my_func "$arg1" "$arg2". Mi única objeción con este ejemplo es que el valor de retorno de "$ @" se pierde, pero eso se soluciona fácilmente. Puede ser deseable envolver "$ @" ()para garantizar que los cambios ambientales no se filtren, con algún costo de rendimiento de bifurcación.
Michael Mol
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.