evalとexecの違いは何ですか?


81

evalそしてexec両方のコマンドを実行するのbashのコマンド(1)に内蔵されています。

またexec、いくつかのオプションがありますが、それが唯一の違いですか?彼らの文脈はどうなりますか?



回答:


124

evalそしてexec全く異なる獣です。(どちらもコマンドを実行しますが、シェルで行うことはすべて実行します。)

$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
    Replace the shell with the given command.

何をexec cmd行い、単に実行するのとまったく同じであるcmd代わりに、別のプロセスであること、実行のため、現在のシェルコマンドで置き換えられることを除き、。内部的に、say /bin/lsを実行するとfork()、子プロセスを作成するために呼び出され、次にexec()子で実行され/bin/lsます。exec /bin/ls一方、フォークはしませんが、シェルを置き換えます。

比較する:

$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo

$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217

echo $$私が開始したシェルのPIDを出力し、リストからシェルから実行されたシェルの/proc/selfPIDが得られlsます。通常、プロセスIDは異なりますがexec、シェルを使用しls、同じプロセスID を持っています。また、execシェルが置き換えられたため、次のコマンドは実行されませんでした。


一方:

$ help eval
eval: eval [arg ...]
    Execute arguments as a shell command.

eval現在のシェルでコマンドとして引数を実行します。言い換えればeval foo bar、ちょうどと同じfoo barです。ただし、変数は実行前に展開されるため、シェル変数に保存されたコマンドを実行できます。

$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo

子プロセス作成されないため、変数は現在のシェルで設定されます。(もちろんeval /bin/ls、普通の古いもの/bin/lsと同じように、子プロセスを作成します。)

または、シェルコマンドを出力するコマンドがあります。実行ssh-agentすると、エージェントがバックグラウンドで起動し、現在のシェルで設定され、子プロセス(ssh実行するコマンド)で使用される可能性のある一連の変数割り当てを出力します。したがって、次のssh-agent方法で開始できます。

eval $(ssh-agent)

そして、現在のシェルは継承する他のコマンドの変数を取得します。


もちろん、cmdたまたま変数にのようなものが含まれているrm -rf $HOME場合、実行eval "$cmd"は望んでいることではありません。文字列内のコマンド置換のようにも、物事が処理されますので、一つはなければならない、本当にへの入力があることを確認してくださいeval、それを使用する前に安全です。

多くの場合、eval誤ってコードとデータを誤って混在させることを回避および回避することが可能です。


@ilkkachuという素晴らしい答えです。ありがとう!
ウィリアンパイシャオ16

evalの使用方法の詳細については、stackoverflow.com
a / 46944004/2079103

1
@clearlight は、最初の場所で使用evalしないことに関する通常の免責事項をこの回答にも追加することを思い出させます。間接的に変数を変更するようなものは、declare/ typeset/ namerefやのような拡張を介して多くのシェルで実行できる${!var}ため、eval実際に回避する必要がない限り、代わりにそれらを使用します。
-ilkkachu

27

exec新しいプロセスを作成しません。現在のプロセスを新しいコマンドに置き換えます。コマンドラインでこれを行うと、シェルセッションが事実上終了します(そしてログアウトするか、ターミナルウィンドウを閉じるかもしれません!)

例えば

ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh% 

ここにいますksh(私の通常のシェル)。始めてbashから、bashの中に入れexec /bin/echoます。プロセスがに置き換えられkshたため、私はその後に戻ったことがわかります。bash/bin/echo


顔のummはksh b / cプロセスに戻ってエコーに置き換えられましたが、それほど意味がありませんか?

14

TL; DR

execコマンドが指定されていない場合、現在のシェルプロセスを新しいものに置き換え、ストリームリダイレクト/ファイル記述子を処理するために使用されます。eval文字列をコマンドとして評価するために使用されます。どちらも実行時に既知の引数を使用してコマンドを構築および実行するために使用できますが、コマンドのexec実行に加えて現在のシェルのプロセスを置き換えます。

エグゼクティブビルイン

構文:

exec [-cl] [-a name] [command [arguments]]

マニュアルによると、このビルトインが指定されたコマンドがある場合

...シェルを置き換えます。新しいプロセスは作成されません。引数はコマンドの引数になります。

つまり、bashPID 1234で実行している場合exec top -u root、そのシェル内で実行すると、topコマンドはPID 1234を持ち、シェルプロセスを置き換えます。

これはどこで便利ですか?ラッパースクリプトとして知られているもの。そのようなスクリプトは引数のセットを構築するか、環境に渡す変数について特定の決定を下し、exec指定されたコマンドでそれ自体を置き換えるために使用し、もちろんラッパースクリプトが途中で構築したものと同じ引数を提供します。

マニュアルには次のようにも書かれています:

コマンドが指定されていない場合、リダイレクトは現在のシェルで有効になります

これにより、現在のシェルの出力ストリームからファイルに何でもリダイレクトできます。これはstdout、コマンドを表示するのではなく、のみを表示するロギングまたはフィルタリングの目的に役立つ場合がありますstderr。たとえば、次のように:

bash-4.3$ exec 3>&1
bash-4.3$ exec > test_redirect.txt
bash-4.3$ date
bash-4.3$ echo "HELLO WORLD"
bash-4.3$ exec >&3
bash-4.3$ cat test_redirect.txt 
2017 05 20 星期六 05:01:51 MDT
HELLO WORLD

この動作により、シェルスクリプトへのログイン、個別のファイルまたはプロセスへのストリームのリダイレクト、およびファイル記述子を含むその他の楽しいものに便利になります。

少なくともbashバージョン4.3 のソースコードレベルでは、 exec組み込みはで定義されていbuiltins/exec.defます。受信したコマンドを解析し、もしあればshell_execve()execute_cmd.cファイルで定義された関数に渡します。

要するに、execCプログラミング言語にはコマンドファミリが存在し、shell_execve()基本的には次のラッパー関数ですexecve

/* Call execve (), handling interpreting shell scripts, and handling
   exec failures. */
int
shell_execve (command, args, env)
     char *command;
     char **args, **env;
{

eval組み込み

bash 4.3のマニュアルの状態(私が強調した内容):

引数は読み取られ、単一のコマンドに連結されます。このコマンドはシェルによって読み取られて 実行され、その終了ステータスがevalの値として返されます。

プロセスの置換は発生しないことに注意してください。exec目標がexecve()機能をシミュレートすることとは異なり、eval組み込み関数は、ユーザーがコマンドラインで入力したかのように、引数を「評価」するだけです。そのため、新しいプロセスが作成されます。

これが役立つ場所 Gilles がこの回答で指摘したように、「... evalはあまり使用されません。シェルによっては、最も一般的な使用法は、実行時まで名前がわからない変数の値を取得することです」。個人的には、ユーザーが現在使用している特定のワークスペースに基づいてコマンドを実行/評価する必要があるUbuntuのいくつかのスクリプトで使用しました。

ソースコードレベルではbuiltins/eval.def、解析された入力文字列で定義され、evalstring()関数に渡されます。

とりわけ、evalできる変数に割り当てている間は、現在のシェル実行環境にとどまるexecことはできません。

$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found

@ ctrl-alt-delorその部分を編集しました。おかげで、おそらく新しいプロセスが生成されますが、PIDは現在のシェルと同じままです。将来的には、特に7ワードフレーズなどの些細なことが問題となっている場合は、コメントや下票を残す代わりに、回答を編集することを検討してください。回答の編集と修正にかかる時間が大幅に短縮され、はるかに役立ちます。
セルギーコロディアズニー

5
新しい子プロセスを作成し、引数を実行して終了ステータスを返します。

あれ?全体のポイントはeval、子プロセスを作成しないことです。私が行った場合

eval "cd /tmp"

シェルでは、その後、現在のシェルはディレクトリを変更します。どちらもexec新しい子プロセスを作成せず、代わりに指定された実行可能ファイルの現在の実行可能ファイル(シェル)を変更します。プロセスID(および開いているファイルなど)は同じままです。とは対照的にeval、実行可能ファイルを見つけられないかロードできないか、引数展開の問題で死ぬことによって失敗した場合をexec除き、呼び出し元のシェルに戻りexecません。

eval基本的には、引数を連結後の文字列として解釈します。つまり、ワイルドカード展開と引数分割の追加レイヤーを実行します。 execそのようなことはしません。


1
元の質問は「evalとexecは両方ともBash(1)の組み込みコマンドであり、コマンドを実行し、新しい子プロセスを作成し、引数を実行し、終了ステータスを返します」と読みました。私は誤った推定を編集しました。
チャールズスチュワート

-1

評価

これらの仕事:

$ echo hi
hi

$ eval "echo hi"
hi

$ exec echo hi
hi

ただし、これらは以下を行いません。

$ exec "echo hi"
bash: exec: echo hi: not found

$ "echo hi"
bash: echo hi: command not found

プロセスイメージの置換

この例execは、呼び出しプロセスのイメージを置き換える方法を示しています。

# Get PID of current shell
sh$ echo $$
1234

# Enter a subshell with PID 5678
sh$ sh

# Check PID of subshell
sh-subshell$ echo $$
5678

# Run exec
sh-subshell$ exec echo $$
5678

# We are back in our original shell!
sh$ echo $$
1234

exec echo $$サブシェルのPIDで実行されたことに注意してください!さらに、完了後、元のsh$シェルに戻りました。

一方、プロセスイメージ置き換えevalませ。むしろ、通常のシェル自体の場合と同じように、指定されたコマンドを実行します。(もちろん、プロセスの生成を必要とするコマンドを実行すると...まさにそれが行われます!)

sh$ echo $$
1234

sh$ sh

sh-subshell$ echo $$
5678

sh-subshell$ eval echo $$
5678

# We are still in the subshell!
sh-subshell$ echo $$
5678

他の例は実際に、このようなIと弱々しい心のために十分に理解しやすい方法でこれを説明していなかったので、私はこの答えを掲載
Mateen Ulhaq

言葉の選択の悪さ:プロセス置換はここで説明しているものとは無関係であると思われる既存の概念です。
muru

@muru「プロセス置換」の方が良いですか?正しい用語が何なのかわかりません。マンページはそれを「プロセスイメージの置き換え」と呼んでいるようです。
Mateen Ulhaq

うーん、知らない。(しかし、「プロセスイメージの置き換え」と聞いたとき、私は思う:exec
muru
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.