web-dev-qa-db-ja.com

Bash:関数をパラメーターとして渡す

関数をBashのパラメーターとして渡す必要があります。たとえば、次のコード:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  eval $1
  echo "after"
}

around x

出力する必要があります:

before
Hello world
after

私はevalがそのコンテキストでは正しくないことを知っていますが、それは単なる例です:)

何か案が?

74
cd1

関数名やその引数の評価を遅らせるような凝ったものが必要なければ、evalは必要ありません。

function x()      { echo "Hello world";          }
function around() { echo before; $1; echo after; }

around x

あなたがしたいことをします。この方法で関数とその引数を渡すこともできます:

function x()      { echo "x(): Passed $1 and $2";  }
function around() { echo before; "$@"; echo after; }

around x 1st 2nd

プリント

before
x(): Passed 1st and 2nd
after
105
Idelic

誰もこの質問に答えたとは思わない。彼は、文字列を順番にエコーできるかどうかを尋ねませんでした。むしろ、質問の作成者は、関数ポインターの動作をシミュレートできるかどうかを知りたがっています。

私がやろうとしていることによく似た答えがいくつかありますが、別の例でそれを拡張したいと思います。

著者から:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  ($1)                   <------ Only change
  echo "after"
}

around x

これを拡張するには、関数x echo "Hello world:$ 1"を使用して、関数の実行が実際にいつ行われるかを示します。関数「x」の名前である文字列を渡します。

function x() {
  echo "Hello world:$1"
}

function around() {
  echo "before"
  ($1 HERE)                   <------ Only change
  echo "after"
}

around x

これを説明するために、文字列「x」が関数around()に渡され、「before」をエコーし​​、関数xを呼び出して(変数$ 1、roundに渡される最初のパラメーター)引数「HERE」を渡し、最後にエコーします。

別の方法として、これは変数を関数名として使用する方法です。変数は実際には関数の名前である文字列を保持し、($ variable arg1 arg2 ...)は引数を渡して関数を呼び出します。下記参照:

function x(){
    echo $3 $1 $2      <== just rearrange the order of passed params
}

Z="x"        # or just Z=x

($Z  10 20 30)

30 10 20で、変数Zに格納されている「x」という名前の関数を実行し、パラメーター10 20および30を渡しました。

変数名を関数に割り当てることで関数を参照する上記のように、関数名を実際に知る代わりに変数を使用できます(これは、プログラムフローを一般化するためにcの非常に古典的な関数ポインターの状況で行うことと似ていますが、 -コマンドライン引数に基づいて作成する関数呼び出しを選択します)。

Bashでは、これらは関数ポインターではなく、後で使用する関数の名前を参照する変数です。

21
uDude

evalを使用する必要はありません

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  var=$($1)
  echo "after $var"
}

around x
16
kurumi

文字列以外の関数に何も渡すことはできません。プロセスの置換は、一種の偽物です。 Bashは、コマンドが展開して完了するまでFIFOを開いたままにする傾向があります。

ここに簡単な愚かなものがあります

_foldl() {
    echo $(($(</dev/stdin)$2))
} < <(tr '\n' "$1" <$3)

# Sum 20 random ints from 0-999
foldl + 0 <(while ((n=RANDOM%999,x++<20)); do echo $n; done)
_

関数はエクスポートできますが、これは最初に表示されるほど面白くありません。主に、スクリプトまたはスクリプトを実行する他のプログラムがデバッグ機能にアクセスできるようにするのに役立ちます。

_(
    id() {
        "$@"
    }

    export -f id
    exec bash -c 'echowrap() { echo "$1"; }; id echowrap hi'
)
_

idは、まだ関数の名前(環境内のシリアル化から自動的にインポートされた)とその引数である文字列のみを取得します。

別の答えに対するPumbaa80のコメントも良い(eval $(declare -F "$1"))が、それらは常にグローバルであるため、関数ではなく配列に主に役立つ。これを関数内で実行する場合、再定義するだけなので、効果はありません。現在のスコープにバインドされているものに依存するクロージャーや部分関数、または「関数インスタンス」の作成には使用できません。せいぜいこれは、他の場所で再定義される文字列に関数定義を格納するために使用できます-しかし、もちろんevalが使用されない限り、これらの関数もハードコーディングできます

基本的に、Bashはこのようには使用できません。

5
ormaaj

より良いアプローチは、関数でローカル変数を使用することです。問題は、呼び出し元に結果をどのように取得するかとなります。 1つのメカニズムは、コマンド置換を使用することです。

function myfunc()
{
    local  myresult='some value'
    echo "$myresult"
}

result=$(myfunc)   # or result=`myfunc`
echo $result

ここでは、結果が標準出力に出力され、呼び出し元はコマンド置換を使用して変数の値をキャプチャします。その後、変数は必要に応じて使用できます。

2

次の行に沿って何かが必要です。

function around()
{
  echo 'before';
  echo `$1`;
  echo 'after';
}

その後、around xを呼び出すことができます

1
Tim O

evalはおそらくそれを達成する唯一の方法です。唯一の本当の欠点は、セキュリティ面です。悪意のあるものが渡されず、呼び出されたい関数のみが呼び出されることを確認する必要があるためです(「;」のような厄介な文字がないことを確認するとともに同様に)。

あなたがコードを呼び出しているのであれば、おそらくevalがそれを行う唯一の方法です。サブコマンド($()および ``)を使用しても機能する可能性のある他の形式のevalがありますが、安全ではなく、より高価であることに注意してください。

0
Wes Hardaker