Übergeben eines Codeblocks als Anon. Funktion

9

Ist es möglich, einen Befehlsblock als anonyme Funktion zu behandeln?

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
}

(Mir ist klar, dass Sie für jeden Block eine Funktion oder Datei erstellen, aber ich finde diese Option in bestimmten Situationen klarer und leichter zu lesen.)

whilemacht es mit do/doneund functionmacht es mit { multiple lines }. Mir ist klar, dass BASH keine anonymen Funktionen hat, aber ist es möglich, mehrere Befehle an eine andere Funktion zu übergeben, wie Sie dies beim Definieren einer Funktion tun können oder while?

dgo.a.
quelle
Meinen Sie damit, dass Sie dekorieren möchten (im Python-Sprachgebrauch) - dh eine Funktion von einer Funktion zurückgeben? Ihr Beispiel ist syntaktisch nicht einmal BASH: Soll wrap_this eine Funktion oder ein Funktionsaufruf sein?
Mel Boyce
Mir ist unklar, was Sie tun möchten. Wie Mel betonte, ist das, was Sie geschrieben haben, sogar syntaktisch gültig, aber es ist mir unklar, wie sich das, was Sie geschrieben haben, auf anonyme Funktionen bezieht.
Chris Down

Antworten:

2

Dies ist die kürzeste Lösung, die ich mir vorstellen kann:

Angesichts dieser Funktionen:

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

Beispiele:

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

(Diese Beispiele sind natürlich nur Verwendungsbeispiele, und in Wirklichkeit wäre dieser Stil nur für kompliziertere Anwendungsfälle sinnvoll.)

Im Allgemeinen kann immer der folgende Stil verwendet werden:

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

Es ist nicht ganz Lambda, aber es ist sehr, sehr nah. Nur ein paar überschüssige Zeichen.

Aber eine der folgenden Abkürzungen funktioniert nicht, soweit ich das beurteilen kann:

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

Leider bashist es nicht sehr gut für diesen Stil geeignet, obwohl einige seiner Funktionen einen sehr funktionalen Stil haben.

Evi1M4chine
quelle
Merkmale von Bash haben funktionalen Stil? Das einzige, was aus der Ferne funktioniert, ist, dass bash (wie so ziemlich jede Shell-Sprache) eine Form der Funktionszusammensetzung unterstützt, indem die Ausgabe eines Befehls in die Eingabe eines anderen geleitet wird. Dies ist jedoch eher ein Merkmal des allgemeinen Designs von Unix, nicht das eigentliche Bash.
kyrill
3

Sie können Code in einem String setzen und übergeben es an evaloder shoder es einfach zu interpolieren.

perform () {
  "$@"
}

perform echo "moo"

Es kann jedoch schnell zu Problemen beim Zitieren kommen.

Tripleee
quelle
1
Eigentlich habe ich gesucht perform { echo "moo" \n echo "moo moo" \n echo "moo moo moo" }. Ich wusste bereits, dass Sie einen Befehl übergeben können. Aber ich suche nach mehreren Zeilen, nicht nur nach einem Befehl oder einer Zeile. Danke, dass du es versucht hast.
dgo.a
3

Nein, bash hat keine anonymen Funktionen. Es ist jedoch möglich, einen Funktionsnamen und Argumente als Zeichenfolgen zu übergeben und von bash aufrufen zu lassen.

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

wrap do_something with_arguments

Dies ist jedoch etwas begrenzt. Der Umgang mit Zitaten kann zum Problem werden. Das Übergeben von mehr als einem Befehl ist ebenfalls eine Komplikation.

David Baggerman
quelle
1
Kein Nachname, alles was Sie tun müssen, ist $*zu"$@"
Glenn Jackman
Mehrzeilige Funktionskörper sollten mit dieser Methode einwandfrei funktionieren.
Glenn, ich habe diese Form vergessen, danke. Das Problem mit dem Angebot wird jedoch nicht vollständig gelöst. Evan, ein einzelner Befehl, der über mehrere Zeilen gewickelt ist, würde gut funktionieren. Ich meinte, mehr als einen Befehl im Textkörper zu haben, wie in den Beispielen in der Frage beschrieben. Ich werde meine Antwort aktualisieren, um beide Kommentare zu beantworten.
David Baggerman
3

Ich habe es geschafft, mit einem evalHack zu machen, was du willst . Damit warnte ich, dass die Bewertung unsicher ist und Sie um jeden Preis vermeiden sollten . Wenn Sie jedoch darauf vertrauen, dass Ihr Code nicht missbraucht wird, können Sie Folgendes verwenden:

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

Auf diese Weise können Sie Code wie folgt ausführen:

wrap_this << EOF
    my function
EOF

Nicht gerade ideal, da der innere Block eine Zeichenfolge ist, aber eine Wiederverwendung erzeugt.

hkupty
quelle
Das ist fantastisch! Ich würde nur Anführungszeichen um das erste hinzufügen, um !!eine Substitution im Heredoc zu verhindern. stackoverflow.com/questions/27920806/…
Saintali