anonとしてコードブロックを渡します。関数


9

コマンドのブロックを無名関数として扱うことは可能ですか?

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
}

(ブロックごとに関数またはファイルを作成すると思いますが、このオプションは特定の状況でより明確で読みやすいと思います。)

whileでそれを行いdo/done、でfunctionそれを行い{ multiple lines }ます。BASHには匿名関数がないことに気づきましたが、関数を定義するときに行うことができるように、複数のコマンドを別の関数に渡すことは可能whileですか?


(Pythonの用語で)装飾したいということですか?つまり、関数から関数を返しますか?あなたの例は、構文的にはBASHでもありません:wrap_thisは関数または関数呼び出しのはずですか?
Mel Boyce 2013年

あなたが何をしたいのか私には分かりません。Melが指摘したように、あなたが書いたものは構文的にも有効ですが、あなたが書いたものが匿名関数とどのように関連しているかは私にはわかりません。
Chris Down

回答:


2

これは私が考えることができる最も短い解決策です:

これらの機能を考えると:

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

例:

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

(もちろん、これらの例は単なる使用例であり、実際には、このスタイルはより複雑なユースケースでのみ意味があります。)

通常、次のスタイルを常に使用できます。

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

そうではありません非常にラムダが、それは非常に非常に近いです。いくつかの余分な文字のみ。

しかし、以下の便利な略語は、私が知る限りでは機能しません。

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

残念ながら、bash一部の機能には非常に機能的なスタイルがありますが、このスタイルにはあま​​り適していません。


bashの機能には機能的なスタイルがありますか?リモートで機能する唯一のものは、bash(ほとんどのシェル言語と同様)が、あるコマンドの出力を別のコマンドの入力にパイプすることによって、ある形式の関数構成をサポートすることです。それは、それ自体がbashではなく、Unixの一般的な設計の機能の詳細です。
キリル

4

私はevalハックであなたがやりたいことをなんとかやりました。これにより、evalは安全ではないので、絶対に避けてくださいと警告しました。そうは言っても、あなたのコードが悪用されないことが信頼できるなら、これを使うことができます:

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

これにより、次のようにコードを実行できます。

wrap_this << EOF
    my function
EOF

内側のブロックは文字列なので、厳密には理想的ではありませんが、再利用を作成します。


これはすごい!!!ヒアドキュメントでの置換を防ぐために、最初の部分を引用符で囲みます。stackoverflow.com/questions/27920806/...
Saintali

3

コードを文字列に入れて、それを渡すevalsh、単純に補間することができます。

perform () {
  "$@"
}

perform echo "moo"

ただし、すぐに、深い引用の問題に陥ることがあります。


1
実は、探していましたperform { echo "moo" \n echo "moo moo" \n echo "moo moo moo" }。1つのコマンドを渡すことができることはすでに知っていました。しかし、私は1つのコマンドや1行だけでなく、複数の行を探しています。試していただきありがとうございます。
dgo.a 2013

3

いいえ、bashには匿名関数はありません。ただし、関数名と引数を文字列として渡し、bashにそれを呼び出させることは可能です。

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

wrap do_something with_arguments

ただし、これには多少の制限があります。クォートの扱いが問題になる可能性があります。複数のコマンドを渡すことも複雑です。


1
ナイトネームではありません。次のように変更$*するだけ"$@"
です

この方法では、複数行の関数本体が正常に機能するはずです。

グレン、私はそのフォームを忘れてしまった、ありがとう。ただし、引用の問題は完全には解決しません。Evan、複数の行にラップされた単一のコマンドは問題なく機能します。質問の例に従って、本文に複数のコマンドを含めることを意味しました。両方のコメントに対処するために回答を更新します。
David Baggerman 2013年

このフォームは、単純な引数を取る関数を定義して、それらの関数の呼び出しを引数として渡す場合に非常にうまく機能します。すなわちwrap my_func "$arg1" "$arg2"。この例での1つの問題は、 "$ @"の戻り値が失われることですが、簡単に修正できます。()いくつかの分岐パフォーマンスコストで、環境の変更がリークしないようにするために、「$ @」をラップすることが望ましい場合があります。
Michael Mol
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.