Passando um bloco de código como um ânon. função

9

É possível tratar um bloco de comandos como uma função 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
}

(Percebo que você cria uma função ou arquivo para cada bloco, mas acho essa opção mais clara e fácil de ler em determinadas situações.)

whilefaz com do/donee functionfaz com { multiple lines }. Sei que o BASH não tem funções anônimas, mas é possível passar vários comandos para outra função, como você pode fazer ao definir uma função ou while?

dgo.a
fonte
Você quer dizer que deseja decorar (na linguagem Python) - ou seja, retornar uma função de uma função? Seu exemplo, sintaticamente, nem é BASH: wrap_this deve ser uma função ou uma chamada de função?
precisa
Não está claro para mim o que você quer fazer. Como Mel apontou, o que você escreveu é sintaticamente válido, mas não está claro para mim como o que você escreveu se relaciona com funções anônimas.
Chris Baixo

Respostas:

2

Esta é a solução mais curta que eu consegui pensar:

Dadas estas funções:

# 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 ]]; }

Exemplos:

# 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/; {}}}}}}}}}}}}}}}}}}}}}}

(Esses exemplos são obviamente apenas exemplos de uso e, na realidade, esse estilo só faria sentido para casos de uso mais complicados.)

Geralmente, o seguinte estilo sempre pode ser usado:

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

Não é bem lambda, mas é muito, muito próximo. Apenas alguns caracteres em excesso.

Mas qualquer uma das abreviaturas de conveniência a seguir não funciona até onde sei:

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

Infelizmente, bashnão é muito adequado para esse estilo, embora alguns de seus recursos tenham um estilo muito funcional.

Evi1M4chine
fonte
Recursos do bash têm estilo funcional? A única coisa remotamente funcional é que o bash (como praticamente qualquer linguagem shell) suporta uma forma de composição de funções, canalizando a saída de um comando para a entrada de outro. Essa é mais uma característica do design geral do Unix, não do bash em si.
kyrill 22/04
4

Eu consegui fazer o que você quer com um evalhack. Com isso, avisei que a avaliação é insegura e você deve evitar a todo custo . Dito isto, se você confiar que seu código não será abusado, você pode usar o seguinte:

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

Isso permite que você execute o código da seguinte maneira:

wrap_this << EOF
    my function
EOF

Não é exatamente o ideal, pois o bloco interno é uma sequência, mas cria reutilização.

hkupty
fonte
Isso é incrível! Gostaria apenas de adicionar aspas ao redor da primeira !!, para evitar a substituição no heredoc. stackoverflow.com/questions/27920806/…
Saintali
3

Você pode colocar o código em um fio e passá-lo para evalou shou simplesmente interpolar-lo.

perform () {
  "$@"
}

perform echo "moo"

Você pode acabar rapidamente com problemas profundos de citações.

triplo
fonte
1
Na verdade, eu estava procurando perform { echo "moo" \n echo "moo moo" \n echo "moo moo moo" }. Eu já sabia que você pode passar um comando. Mas estou procurando várias linhas, não apenas um comando ou uma linha. Obrigado por tentar.
dgo.a
3

Não, o bash não possui funções anônimas. No entanto, é possível passar um nome de função e argumentos como seqüências de caracteres e ser chamado por bash.

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

wrap do_something with_arguments

No entanto, isso é um pouco limitado. Lidar com a citação pode se tornar um problema. Passar mais de um comando também é uma complicação.

David Baggerman
fonte
1
Não é um nightname, tudo que você tem a fazer é mudar $*para"$@"
Glenn Jackman
Os corpos funcionais de várias linhas devem funcionar bem com esse método.
Glenn, esqueci esse formulário, obrigado. Porém, não resolve completamente o problema de cotação. Evan, um único comando envolvido em várias linhas funcionaria bem, eu quis dizer ter mais de um comando no corpo, conforme os exemplos da pergunta. Vou atualizar minha resposta para abordar os dois comentários.
David Baggerman
Este formulário funciona muito bem se você definir funções que usam argumentos simples e depois passar chamadas para essas funções como argumentos. Ou seja wrap my_func "$arg1" "$arg2". Meu único problema com este exemplo é que o valor de retorno "$ @" é perdido, mas isso é facilmente remediado. Pode ser desejável agrupar "$ @" ()para garantir que as mudanças ambientais não vazem, com um custo de desempenho de bifurcação.
Michael Mol