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

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

evalどちらexecもコマンドを実行するbash(1)コマンドに組み込まれています。

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

答え1

evalexec全く違う獣です。 (両方ともコマンドを実行することを除いて、シェルで実行されるすべての操作も同じです。)

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

別のプロセスを実行するのではなく、現在のシェルがコマンドに置き換えられることを除いて、exec cmdrunとまったく同じことを行います。cmd内部的にsayを実行すると、子プロセスが作成され、/bin/ls子プロセスで実行されます。一方、それはfork()exec()/bin/lsexec /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/self私たちが実行したシェルのPIDが表示されます。ls通常、プロセスIDは異なりますが、execシェルとls同じプロセスIDを持ちます。また、execシェルが交換されたため、次のコマンドは実行されません。


一方:

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

eval引数は現在のシェルでコマンドとして実行されます。つまり、eval foo barjust と同じですfoo bar。ただし、変数は実行前に拡張されるため、シェル変数に格納されているコマンドを実行できます。

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

そうだろういいえこの変数が現在のシェルに設定されるように子プロセスを作成します。 (eval /bin/lsもちろん、サブプロセスは既存のプロセスと同様に作成されます/bin/ls。)

あるいは、シェルコマンドを出力するコマンドを持つこともできます。 Runningはバックグラウンドでエージェントを起動し、現在のシェルで設定し、サブプロセス(実行するコマンド)ssh-agentで使用できる複数の変数割り当てを出力します。sshしたがって、ssh-agent次から始めてください。

eval $(ssh-agent)

そして、現在のシェルは他のコマンドによって継承された変数を取得します。


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

誤ってevalコードとデータが誤って混在するのを防ぐことがよくあります。

答え2

exec新しいプロセスは作成されません。それ変える新しいコマンドを使用する現在のプロセスです。コマンドラインでこれを行うと、シェルセッションが効果的に終了します(ログアウトしたり、ターミナルウィンドウを閉じたりすることもできます)。

例えば

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

ここにありますksh(私の一般的な殻)。私は始めてbashからbash exec /bin/echoIで始めました。プロセスがに置き換えられたkshため、後で返されたことがわかります。bash/bin/echo

答え3

長い話を短く

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

組み込み実行

通事論:

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

マニュアルによると、この組み込みコマンドが指定されているかどうか

...ケースを交換してください。新しいプロセスは作成されません。これらのパラメーターはコマンドのパラメーターになります。

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

これの用途は何ですか?ラッパースクリプトということから。これらのスクリプトは、パラメータセットを構築するか、環境に渡す変数を決定し、指定されたコマンドで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

簡単に言えば、Cプログラミング言語には、基本的に次のためのラッパー関数である一連のexecコマンドがあります。shell_execve()execve

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

評価内蔵

bash 4.3のマニュアルには次のように記載されています(強調項目が追加されています)。

argsを読み込み、一緒に接続してコマンドを形成します。その後、コマンドを読んで シェルによって実行される、終了ステータスが eval 値として返されます。

プロセスの交換は発生しません。関数をexecシミュレートする目的とは異なり、組み込み関数は、ユーザーがコマンドラインに引数を入力するかのように引数を「評価」するためにのみ使用されます。このようにして新しいプロセスが作成されます。execve()eval

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

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

何よりも、次のことがevalできます。変数の割り当て現在、シェル実行環境を維持していますが、exec次のことはできません。

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

答え4

評価する

これらのタスクは次のとおりです。

$ 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

関連情報