コマンドとしての変数; eval vs bash -c


41

私が作っbashスクリプトの誰かに読んでいたし、私は著者がコマンドとして変数を評価するためにevalを使用していないことに気づいた
著者が使用します

bash -c "$1"

の代わりに

eval "$1"

evalを使用することが推奨される方法であり、おそらくいずれにせよ高速であると思います。本当?
2つの間に実際的な違いはありますか?両者の顕著な違いは何ですか?


場合によっては、どちらも使わずに逃げることができます。e='echo foo'; $eうまく動作します。
デニス

回答:


40

eval "$1"現在のスクリプトでコマンドを実行します。現在のスクリプトのシェル変数の設定と使用、現在のスクリプトの環境変数の設定、現在のスクリプトの関数の設定と使用、現在のスクリプトの現在のディレクトリ、umask、制限、およびその他の属性の設定などが可能です。bash -c "$1"環境変数、ファイル記述子、およびその他のプロセス環境を継承する(ただし、変更を送信しない)完全に独立したスクリプトでコマンドを実行しますが、内部シェル設定(シェル変数、関数、オプション、トラップなど)は継承しません。

(eval "$1")サブシェルでコマンドを実行する別の方法があります。呼び出しスクリプトからすべてを継承しますが、変更を送信しません。

例えば、変数があると仮定dirエクスポートされない$1cd "$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/directorycd ""現在のディレクトリを変更しないため)の内容をリストし、を出力し/starting/directoryます。

ありがとう。(eval "$ 1")について知りませんでしたが、ソースとは違うのですか?
whoami

1
@whoamiは(eval "$1")とは何の関係もありませんsource。それはちょうどの組み合わせだ(…)evalsource fooはとほぼ同等eval "$(cat foo)"です。
ジル「SO-停止されて悪」

我々は...と同時に、私たちの答えを書いている必要があります
mikeserv

@whoamiの主な違いevalとは.dotつまりevalと作品の引数.dotして動作しますファイル。
mikeserv 14

両方に感謝します。私の以前のコメントは...私は再びそれを読むことを今ちょっと愚かなように見える
whoamiは

23

間の最も重要な違い

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

しかし、あなたはそれをまったく必要としますか?

実際にどちらかを使用する唯一の原因は、変数が実際に別の変数を割り当てたり評価したりする場合、またはワードスプリットが出力にとって重要な場合です。

例えば:

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

を使用するevalsh、シェルが展開の結果で2番目のパスを取得し、それらを潜在的なコマンドとしても評価すると、引用符が違いを生じます。次のこともできます。

. <<VAR /dev/fd/0
    ${var:=echo "this is var"}
#END
VAR

出力

this is var

5

私は簡単なテストを行いました:

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高速です。のmanページからeval

evalユーティリティは、引数を文字で区切って連結し、コマンドを作成します。構築されたコマンドは、シェルによって読み取られて実行されます。

bash -cもちろん、bashシェルでコマンドを実行します。1つのメモ:を使用してシェルが組み込まれている/bin/echoため、新しいプロセスを開始する必要がないことを意味します。交換してのためのテスト、それがかかりました。それはほぼ同じです。Hovever は、実行可能ファイルの実行が高速です。ここでの主な違いは、新しいシェルを起動せず(現在のシェルでコマンドを実行する)、新しいシェルを起動してから新しいシェルでコマンドを実行することです。新しいシェルの起動には時間がかかります。そのため、が遅いのです。echobash/bin/echoechobash -c1.28sevalevalbash -cbash -ceval


OPはnot と比較bash -cしたいと思います。evalexec
ジョセフR.

@JosephR。おっとっと!それを変更します。
PlasmaPower 14

1
@JosephR。今すぐ修正する必要があります。また、私はもう少しテストし、やり直しbash -cていないことを悪い...
PlasmaPower

3
これは事実ですが、コマンドが異なる環境で実行されるという根本的な違いを見逃しています。bashの新しいインスタンスの起動が遅くなることは明らかです。これは興味深い観察ではありません。
ジル「SO-悪であるのをやめる」
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.