Passare un blocco di codice come anone. funzione


9

È possibile trattare un blocco di comandi come una funzione anonima?

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
}

(Mi rendo conto che crei una funzione o un file per ogni blocco, ma trovo questa opzione più chiara e più facile da leggere in determinate situazioni.)

whilelo fa con do/donee lo functionfa con { multiple lines }. Mi rendo conto che BASH non ha funzioni anonime, ma è possibile passare più comandi a un'altra funzione, come si può fare quando si definisce una funzione o while?


Vuoi dire che vuoi decorare (nel linguaggio di Python) - cioè, restituire una funzione da una funzione? Il tuo esempio, sintatticamente, non è nemmeno BASH: wrap_questo dovrebbe essere una funzione o una chiamata di funzione?
Mel Boyce,

Non mi è chiaro cosa tu voglia fare. Come ha sottolineato Mel, ciò che hai scritto è anche sintatticamente valido, ma non mi è chiaro come ciò che hai scritto si riferisca a funzioni anonime.
Chris Down,

Risposte:


2

Questa è la soluzione più breve a cui potrei pensare:

Date queste funzioni:

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

Esempi:

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

(Questi esempi sono ovviamente solo esempi di utilizzo e, in realtà, questo stile avrebbe senso solo per casi d'uso più complicati.)

In genere, è sempre possibile utilizzare il seguente stile:

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

Non è proprio lambda, ma è molto vicino. Solo pochi personaggi in eccesso.

Ma una delle seguenti abbreviazioni di convenienza non funziona per quanto ne so:

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

Sfortunatamente, bashnon è molto adatto per questo stile, anche se alcune delle sue caratteristiche hanno uno stile molto funzionale.


Le caratteristiche di bash hanno uno stile funzionale? L'unica cosa a distanza funzionale è che bash (come praticamente qualsiasi linguaggio di shell) supporta una forma di composizione di funzioni inviando l'output di un comando all'input di un altro. Questa è più una caratteristica del design generale di Unix, non bash di per sé.
Kyrill,

4

Sono riuscito a fare quello che vuoi con un evalhack. Con questo ho avvertito che eval è insicuro e dovresti evitare a tutti i costi . Detto questo, se ritieni che il tuo codice non verrà abusato, puoi utilizzare questo:

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

Ciò consente di eseguire il codice come segue:

wrap_this << EOF
    my function
EOF

Non esattamente ideale, poiché il blocco interno è una stringa, ma crea riutilizzo.


Questo è bellissimo! Vorrei solo aggiungere delle virgolette alla prima !!per evitare la sostituzione in eredità. stackoverflow.com/questions/27920806/...
Saintali

3

È possibile inserire il codice in una stringa e passarlo a evalo sho semplicemente interpolare esso.

perform () {
  "$@"
}

perform echo "moo"

Tuttavia, puoi finire rapidamente in problemi di preventivo.


1
In realtà, stavo cercando perform { echo "moo" \n echo "moo moo" \n echo "moo moo moo" }. Sapevo già che puoi passare un comando. Ma sto cercando più righe, non solo un comando o una riga. Grazie comunque per averci provato.
dgo.a,

3

No, bash non ha funzioni anonime. È comunque possibile passare un nome di funzione e argomenti come stringhe e fare in modo che bash lo chiami.

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

wrap do_something with_arguments

Questo è comunque piuttosto limitato. Gestire i preventivi può diventare un problema. Passare più di un comando è anche una complicazione.


1
Non è un nome da notte, tutto quello che devi fare è cambiare $*in"$@"
glenn jackman,

Gli organismi con funzioni multilinea dovrebbero funzionare bene con questo metodo.

Glenn, mi sono dimenticato di quella forma, grazie. Tuttavia, non risolve completamente il problema di quotazione. Evan, un singolo comando racchiuso su più righe avrebbe funzionato bene, intendevo avere più di un comando nel corpo secondo gli esempi nella domanda. Aggiornerò la mia risposta per rispondere a entrambi i commenti.
David Baggerman,

Questo modulo funziona molto bene se si prende cura di definire funzioni che accettano argomenti semplici e quindi si passano chiamate a tali funzioni come argomenti. Vale a dire wrap my_func "$arg1" "$arg2". Il mio unico cavillo con questo esempio è che il valore di ritorno di "$ @" viene perso, ma è facilmente risolvibile. Potrebbe essere preferibile racchiudere "$ @" ()per garantire che i cambiamenti ambientali non perdano, a un certo costo di prestazioni di fork.
Michael Mol,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.