eval
そしてexec
両方のコマンドを実行するのbashのコマンド(1)に内蔵されています。
またexec
、いくつかのオプションがありますが、それが唯一の違いですか?彼らの文脈はどうなりますか?
eval
そしてexec
両方のコマンドを実行するのbashのコマンド(1)に内蔵されています。
またexec
、いくつかのオプションがありますが、それが唯一の違いですか?彼らの文脈はどうなりますか?
回答:
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/self
PIDが得られ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
誤ってコードとデータを誤って混在させることを回避および回避することが可能です。
eval
しないことに関する通常の免責事項をこの回答にも追加することを思い出させます。間接的に変数を変更するようなものは、declare
/ typeset
/ nameref
やのような拡張を介して多くのシェルで実行できる${!var}
ため、eval
実際に回避する必要がない限り、代わりにそれらを使用します。
exec
新しいプロセスを作成しません。現在のプロセスを新しいコマンドに置き換えます。コマンドラインでこれを行うと、シェルセッションが事実上終了します(そしてログアウトするか、ターミナルウィンドウを閉じるかもしれません!)
例えば
ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh%
ここにいますksh
(私の通常のシェル)。始めてbash
から、bashの中に入れexec /bin/echo
ます。プロセスがに置き換えられksh
たため、私はその後に戻ったことがわかります。bash
/bin/echo
exec
コマンドが指定されていない場合、現在のシェルプロセスを新しいものに置き換え、ストリームリダイレクト/ファイル記述子を処理するために使用されます。eval
文字列をコマンドとして評価するために使用されます。どちらも実行時に既知の引数を使用してコマンドを構築および実行するために使用できますが、コマンドのexec
実行に加えて現在のシェルのプロセスを置き換えます。
構文:
exec [-cl] [-a name] [command [arguments]]
マニュアルによると、このビルトインが指定されたコマンドがある場合
...シェルを置き換えます。新しいプロセスは作成されません。引数はコマンドの引数になります。
つまり、bash
PID 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
ファイルで定義された関数に渡します。
要するに、exec
Cプログラミング言語にはコマンドファミリが存在し、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のマニュアルの状態(私が強調した内容):
引数は読み取られ、単一のコマンドに連結されます。このコマンドはシェルによって読み取られて 実行され、その終了ステータスが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
新しい子プロセスを作成し、引数を実行して終了ステータスを返します。
あれ?全体のポイントはeval
、子プロセスを作成しないことです。私が行った場合
eval "cd /tmp"
シェルでは、その後、現在のシェルはディレクトリを変更します。どちらもexec
新しい子プロセスを作成せず、代わりに指定された実行可能ファイルの現在の実行可能ファイル(シェル)を変更します。プロセスID(および開いているファイルなど)は同じままです。とは対照的にeval
、実行可能ファイルを見つけられないかロードできないか、引数展開の問題で死ぬことによって失敗した場合をexec
除き、呼び出し元のシェルに戻りexec
ません。
eval
基本的には、引数を連結後の文字列として解釈します。つまり、ワイルドカード展開と引数分割の追加レイヤーを実行します。 exec
そのようなことはしません。
評価
これらの仕事:
$ 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
exec
)