誰かが作成したbashスクリプトを読んでいて、作成者がevalを使用して変数をコマンドとして評価していないことに気付きました
著者が使用した
bash -c "$1"
の代わりに
eval "$1"
私はevalの使用が推奨される方法であり、とにかく高速だと思います。本当?
2つの間に実際的な違いはありますか? 2つの間の顕著な違いは何ですか?
_eval "$1"
_は、現在のスクリプトでコマンドを実行します。現在のスクリプトからのシェル変数の設定と使用、現在のスクリプトの環境変数の設定、現在のスクリプトからの関数の設定と使用、現在のスクリプトの現在のディレクトリ、umask、制限およびその他の属性の設定などを行うことができます。 _bash -c "$1"
_は、環境変数、ファイル記述子、およびその他のプロセス環境を継承する(ただし、変更を送信しない)完全に別のスクリプトでコマンドを実行しますが、内部シェル設定(シェル変数、関数、オプション、トラップ)を継承しませんなど)。
別の方法として、サブシェルでコマンドを実行する_(eval "$1")
_があります。これは、呼び出し元のスクリプトからすべてを継承しますが、変更を送信しません。
たとえば、変数dir
がエクスポートされておらず、_$1
_が_cd "$foo"; ls
_であるとすると、次のようになります。
cd /starting/directory; foo=/somewhere/else; eval "$1"; pwd
_は、_/somewhere/else
_の内容をリストし、_/somewhere/else
_を出力します。cd /starting/directory; foo=/somewhere/else; (eval "$1"); pwd
は、_/somewhere/else
_の内容をリストし、_/starting/directory
_を出力します。cd /starting/directory; foo=/somewhere/else; bash -c "$1"; pwd
_は、_/starting/directory
_の内容をリストし(_cd ""
_は現在のディレクトリを変更しないため)、_/starting/directory
_を出力します。間の最も重要な違い
bash -c "$1"
そして
eval "$1"
前者はサブシェルで実行され、後者は実行されないということです。そう:
set -- 'var=something'
bash -c "$1"
echo "$var"
#there doesn't seem to be anything here
set -- 'var=something'
eval "$1"
echo "$var"
something
しかし、なぜこのように実行可能ファイルbash
を使用するのか、私にはわかりません。呼び出す必要がある場合は、POSIX保証の組み込みsh
を使用してください。または、環境を保護する場合は(subshell eval)
。
個人的には、何よりもシェルの.dot
を好みます。
printf 'var=something%d ; echo "$var"\n' `seq 1 5` | . /dev/fd/0
something1
something2
something3
something4
something5
実際にどちらかを使用する唯一の原因は、変数が実際に別の変数を割り当てたり評価したりする場合、またはWord分割が出力にとって重要な場合です。
例えば:
var='echo this is var' ; $var
this is var
これは機能しますが、echo
が引数の数を気にしないためです。
var='echo "this is var"' ; $var
"this is var"
見る?シェルの$var
の展開の結果がquote-removal
に対して評価されないため、二重引用符が付きます。
var='printf %s\\n "this is var"' ; $var
"this
is
var"
ただし、eval
またはsh
の場合:
var='echo "this is var"' ; eval "$var" ; sh -c "$var"
this is var
this is var
eval
またはsh
を使用すると、シェルは展開の結果で2回目のパスを取得し、それらを潜在的なコマンドとしても評価するため、引用符が違いを生みます。次のこともできます:
. <<VAR /dev/fd/0
${var:=echo "this is var"}
#END
VAR
this is var
私は簡単なテストをしました:
time bash -c 'for i in {1..10000}; do bash -c "/bin/echo hi"; done'
time bash -c 'for i in {1..10000}; eval "/bin/echo hi"; done'
(はい、わかっています。ループを実行するためにbash -cを使用しましたが、違いはないはずです)。
結果:
eval : 1.17s
bash -c : 7.15s
したがって、eval
の方が高速です。 eval
のmanページから:
Evalユーティリティは、引数を連結し、それぞれを文字で区切ってコマンドを作成します。構築されたコマンドは、シェルによって読み取られ、実行されます。
bash -c
はもちろん、bashシェルでコマンドを実行します。注:echo
はbash
に組み込まれたシェルであり、新しいプロセスを開始する必要がないため、/bin/echo
を使用しました。 /bin/echo
テストをbash -c
からecho
に置き換えると、1.28s
かかりました。それはほぼ同じです。ただし、eval
は実行可能ファイルを実行する方が高速です。ここでの主な違いは、eval
は新しいシェルを開始しない(現在のシェルでコマンドを実行する)のに対し、bash -c
は新しいシェルを開始し、新しいシェルでコマンドを実行することです。新しいシェルの開始には時間がかかるため、bash -c
はeval
よりも低速です。