更新:一部の人々は、evalを-決して-使用すべきではないと述べています。同意しません。破損した入力がに渡される可能性がある場合、リスクが発生すると思いますeval
。ただし、それが危険ではない多くの一般的な状況があるため、どのような場合でもevalの使用方法を知っておく価値があります。このスタックオーバーフローの回答は、evalのリスクとevalの代替案を説明しています。最終的には、evalが安全で効率的に使用できるかどうか、いつ使用するかを決定するのはユーザーの責任です。
bash eval
ステートメントを使用すると、bashスクリプトによって計算または取得されたコード行を実行できます。
おそらく最も簡単な例は、別のbashスクリプトをテキストファイルとして開き、テキストの各行を読み取り、eval
それらを順番に実行するために使用するbashプログラムでしょう。source
インポートされたスクリプトのコンテンツに対して何らかの変換(フィルタリングや置換など)を実行する必要がない限り、これは基本的にbash ステートメントと同じ動作です。
必要になることはめったにありませんがeval
、他の変数に割り当てられた文字列に名前が含まれている変数を読み書きすると便利です。たとえば、コードのフットプリントを小さく保ち、冗長性を回避しながら、変数のセットに対してアクションを実行する場合。
eval
概念的には単純です。ただし、bash言語の厳密な構文、およびbashインタープリターの解析順序は微妙に異なり、eval
わかりにくく、使用または理解が困難なように見えます。ここに必須事項があります:
渡される引数eval
は、実行時に計算される文字列式です。eval
引数の最終的な解析結果を実際のものとして実行しますスクリプトののコード行します。
構文と解析順序は厳格です。結果がbashコードの実行可能な行ではない場合、スクリプトのスコープでは、プログラムはeval
ガベージを実行しようとするステートメントでます。
テストするときは、eval
ステートメントを置き換えて、echo
何が表示されるかを確認できます。それが現在のコンテキストで正当なコードである場合、それを実行することeval
は機能します。
次の例は、evalの動作を明確にするのに役立ちます...
例1:
eval
「通常の」コードの前のステートメントはNOPです
$ eval a=b
$ eval echo $a
b
上記の例では、最初のeval
ステートメントには目的がなく、削除できます。eval
コードには動的な側面がないため、つまり最初の行では意味がありません。つまり、すでにbashコードの最終行に解析されているため、bashスクリプトの通常のコードステートメントと同じになります。2番目eval
も意味$a
がありません。これは、同等のリテラル文字列に変換する解析ステップがありますが、間接指定がないためです(たとえば、実際の bash名詞またはbash-heldスクリプト変数の文字列値による参照がないため)、同じように動作します。eval
接頭辞なしのコード行として。
例2:
文字列値として渡された変数名を使用して変数割り当てを実行します。
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
にした場合echo $key=$val
、出力は次のようになります。
mykey=myval
これは、文字列解析の最終結果であり、evalによって実行されるものであり、したがって、最後のechoステートメントの結果です...
例3:
例2に間接を追加する
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
上記は、前の例よりも少し複雑で、bashの解析順序と特殊性に大きく依存しています。eval
ラインはおおよそ次の順序で内部的に解析さになるだろう(次の文はちょうど文が最終的な結果に到達するための内部のステップに分けなるだろうどのように表示するように試みるように、擬似コードではなく、実際のコードであることに注意してください)。
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
想定される解析順序でevalの処理が十分に説明されない場合、3番目の例では、何が起こっているのかを明確にするために、解析をより詳細に説明できます。
例4:
名前が文字列に含まれているvar 自体に文字列値が含まれているかどうかを確認します。
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
最初の反復では:
varname="myvarname_a"
バッシュは、引数を解析しeval
て、eval
実行時に、文字通り、これを見ています:
eval varval=\$$myvarname_a
次の擬似コードは、bashが上記の実際のコード行をどのように解釈して、によって実行される最終的な値に到達するかを示しています。(次の行は説明的なもので、正確なbashコードではありません):eval
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
すべての解析が完了すると、結果が実行され、その効果は明白です。eval
それ自体、特に不思議なことは何もなく、その引数の解析には複雑さが伴うことを示しています。
varval="User-provided"
上記の例の残りのコードは、$ varvalに割り当てられた値がnullかどうかを確認するテストを行い、nullの場合は、ユーザーに値を入力するように求めます。
$($n)
実行さ$n
れます。1
存在しないコマンドを実行しようとします。