この特定のケースでexecは、パイプラインにあります。一連のパイプラインコマンドを実行するには、シェルは最初にフォークしてサブシェルを作成する必要があります。(具体的には、パイプを作成してからフォークする必要があります。これにより、パイプの「左側」で実行されるすべてのものが、パイプの「右側」にあるものに出力を送信できるようになります。)
これが実際に起こっていることを確認するには、以下を比較してください。
{ ls; echo this too; } | cat
と:
{ exec ls; echo this too; } | cat
前者はサブシェルlsを離れることなく実行されるため、このサブシェルはまだ実行されていechoます。後者はサブシェルlsを残すことによって実行されます。したがって、サブシェルは実行するために存在しなくなり、echo印刷this tooされません。
(中括弧を使用すると、{ cmd1; cmd2; }通常、括弧を使用して取得するサブシェルフォークアクションが抑制されますが(cmd1; cmd2)、パイプの場合、フォークは、いわば「強制」されます。)
現在のシェルのリダイレクトは、単語の後に「実行するものがない」場合にのみ発生しますexec。したがって、たとえば、exec >stdout 4<input 5>>append現在のシェルを変更しますが、exec foo >stdout 4<input 5>>appendコマンドを実行しようとしfooます。[注:これは厳密には正確ではありません。補遺を参照してください。]
興味深いことに、対話型シェルでexec foo >outputは、コマンドがないために失敗した後foo、シェルは残りますが、stdoutはファイルにリダイレクトされたままになりますoutput。(で回復できますexec >/dev/tty。スクリプトでは、失敗するとexec fooスクリプトが終了します。)
@ Pumbaa80への帽子の先端で、ここにさらにもっと例証的な何かがあります:
#! /bin/bash
shopt -s execfail
exec ls | cat -E
echo this goes to stdout
echo this goes to stderr 1>&2
(注:「印刷されていない文字を認識できるように表示する」ための便利な方法であるcat -E、通常のから簡略化されています)。cat -vETこのスクリプトを実行すると、からの出力lsがcat -E適用されます(Linuxでは、行の終わりが$記号として表示されます)が、stdoutおよびstderr(残りの2行)に送信された出力はリダイレクトされません。に変更し| cat -E、> outスクリプトの実行後、ファイルの内容を確認しますout。最後echoの2つはそこにありません。
ls次に、をfoo(または見つからない他のコマンド)に変更して、スクリプトを再実行します。今回の出力は次のとおりです。
$ ./demo.sh
./demo.sh: line 3: exec: foo: not found
this goes to stderr
これで、ファイルの最初の行outで作成された内容が含まれます。echo
これにより、exec「実際に行うこと」が可能な限り明白になります(ただし、Albert Einsteinがそれを述べていないため、これ以上明白ではありません:-))。
通常、シェルが「単純なコマンド」を実行する場合(正確な定義についてはマニュアルページを参照してください。ただし、これは特に「パイプライン」内のコマンドを除外します)、、などで指定されたI/Oリダイレクト操作を準備し<ます>。必要なファイルを開いてオンにします。次に、シェルが呼び出し(または、基盤となるOS、構成などに依存forkする同等の、より効率的なバリアント)、子プロセスで、開いているファイル記述子を(呼び出しまたは同等のものを使用して)再配置して、目的の最終的な配置を実現します。オープン記述子をfd1(stdout)に移動し、オープン記述子をfd6に移動します。vforkclonedup2> out6> out
execただし、キーワードを指定すると、シェルはforkステップを抑制します。通常どおり、すべてのファイルのオープンとファイル記述子の再配置を行いますが、今回は、後続のすべてのコマンドに影響します。最後に、すべてのリダイレクトを実行した後、シェルexecve()はコマンドがあれば(システムコールの意味で)コマンドを試行します。コマンドがない場合、またはexecve()呼び出しが失敗し、シェルが実行を継続することになっている場合(インタラクティブであるか、設定済みexecfail)、シェルの兵士がオンになります。成功すると、シェルはexecve()存在しなくなり、新しいコマンドに置き換えられます。が設定されておらず、シェルが対話型でない場合execfail、シェルは終了します。
command_not_found_handle(シェル関数の複雑さも追加されexecています。テスト結果に基づいて、bashは実行を抑制しているようです。exec一般に、キーワードはシェルがそれ自体の関数を認識しないようにします。つまり、シェル関数fがある場合f、単純なコマンドは、サブシェルで実行するのと同じようにシェル関数を実行します(f)が、実行する(exec f)とスキップされます。)
なぜ
ls>out1 ls>out22つのファイルを作成するのか(の有無にかかわらず
exec)、これは十分に単純です。シェルは各リダイレクトを開き、を使用
dup2してファイル記述子を移動します。通常のリダイレクトが2つある場合
>、シェルは両方を開き、最初のリダイレクトをfd 1(stdout)に移動し、次に2番目のリダイレクトをfd 1(stdout)に移動して、プロセスの最初のリダイレクトを閉じます。最後に、を実行します。これは
ls ls、を削除した後に残っているため
>out1 >out2です。、という名前のファイルがない限り
ls、
lsコマンドはstderrに文句を言い、stdoutには何も書き込みません。