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


91

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

function x() {
  echo "Hello world"
}

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

around x

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

before
Hello world
after

evalはその文脈では正しくないことを知っていますが、それは単なる例です:)

何か案が?

回答:


126

関数名やその引数の評価を遅らせるような特別なことは何も必要ないのなら、あなたは必要ありません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

2
別のy()関数がある場合、x 1st 2nd y 1st 2ndあたりで実行できますか?xとyが約の引数であり、1番目と2番目がxとyの引数であることをどのように知るのですか?
techguy20 0019

また、機能語は省略できます。
jasonleonhard

ただし、名前間隔は設定されません。つまり、メソッド内にメソッドがあり、そのfunction単語を保持している場合、最上位のメソッドを実行するまで、それらの内部メソッドにアクセスできません。
jasonleonhard

29

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

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

著者から:

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」は「before」をエコーする関数around()に渡され、引数「HERE」を渡して関数xを呼び出し(変数$ 1を介して、最初のパラメーターはaroundに渡されます)、最後にエコーします。

別の余談ですが、これは変数を関数名として使用する方法です。変数は実際には関数の名前である文字列を保持し、($ 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」という名前の関数を実行し、パラメーター1020と30を渡しました。

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

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


この答えは素晴らしいです。すべての例のbashスクリプトを作成し、実行しました。私はまた、あなたが「変化だけ」のメーカーを作った方法が本当に好きで、それは大いに役立ちました。最後から2番目の段落のつづりが間違っています:「Aabove」
JMI MADISON

タイプミスが修正されました。ありがとう@JMADISON
uDude 2017

7
なぜそれを包むの()ですか?それはサブシェルを開始しませんか?
horseyguy

17

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

function x() {
  echo "Hello world"
}

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

around x

5

文字列以外の関数に何も渡すことはできません。プロセスの置換は、一種の偽物である可能性があります。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はこのように使用することはできません。


2

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

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

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

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


1

次のようなものが必要です。

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

その後、電話をかけることができます around x


-1

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

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


evalはそれを行う唯一の方法です。
2011

1
eval $1if declare -F "$1" >/dev/null; then eval $1; fi
user123444555621 2011

2
...またはさらに良い:eval $(declare -F "$1")
user123444555621 2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.