this によると、中括弧の間にコマンドのリストを配置すると、現在のシェルコンテキストでリストが実行されます。サブシェルは作成されません。
ps
を使用してこの動作を確認する
これは、コマンドラインで直接実行されるプロセスパイプラインのプロセス階層です。 4398はログインシェルのPIDです。
sleep 2 | ps -H;
PID TTY TIME CMD
4398 pts/23 00:00:00 bash
29696 pts/23 00:00:00 sleep
29697 pts/23 00:00:00 ps
コマンドラインで直接実行される中括弧間のプロセスパイプラインのプロセス階層に従います。 4398はログインシェルのPIDです。 これは、すべてが現在のシェルコンテキストで実行されることを証明する上記の階層に似ています:
{ sleep 2 | ps -H; }
PID TTY TIME CMD
4398 pts/23 00:00:00 bash
29588 pts/23 00:00:00 sleep
29589 pts/23 00:00:00 ps
さて、これはパイプラインのsleep
自体が中括弧の中に配置されている場合のプロセス階層です(したがって、全体で2レベルの中括弧)。
{ { sleep 2; } | ps -H; }
PID TTY TIME CMD
4398 pts/23 00:00:00 bash
29869 pts/23 00:00:00 bash
29871 pts/23 00:00:00 sleep
29870 pts/23 00:00:00 ps
なぜ、中括弧内のコマンドが現在のシェルコンテキストで実行されるとドキュメントに記載されている場合、bash
が3番目のケースでsleep
を実行するサブシェルを作成する必要があるのですか?
パイプラインでは、すべてのコマンドが同時に実行され(stdout/stdinがパイプで接続されているため)、異なるプロセスで実行されます。
に
cmd1 | cmd2 | cmd3
3つのコマンドはすべて異なるプロセスで実行されるため、少なくとも2つは子プロセスで実行する必要があります。一部のシェルは現在のシェルプロセスでそれらの1つを実行します(read
のように組み込みの場合、またはパイプラインがスクリプトの最後のコマンドである場合)が、bash
はそれらをすべて個別のプロセスで実行します(ただし、最近のlastpipe
バージョンのbash
オプションを使用する場合と、特定の条件下で)。
{...}
グループコマンド。そのグループがパイプラインの一部である場合、単純なコマンドと同様に、別のプロセスで実行する必要があります。
に:
{ a; b "$?"; } | c
それを評価するためにシェルが必要ですa; b "$?"
は別のプロセスなので、サブシェルが必要です。シェルは、そのグループで実行される最後のコマンドであるため、b
をフォークしないことで最適化できます。一部のシェルはそれを行いますが、明らかにbash
ではありません。
中かっこを入れ子にすると、新しいサブシェルを呼び出す必要があるスコープレベルをさらに作成していることになります。この効果は、ps -H
出力のBashの2番目のコピーで確認できます。
中括弧の最初のレベルで規定されているプロセスのみが、元のBashシェルのスコープ内で実行されます。ネストされた中括弧は、独自のスコープ付きBashシェルで実行されます。
$ { { { sleep 20; } | sleep 20; } | ps -H; }
PID TTY TIME CMD
29190 pts/1 00:00:00 bash
5012 pts/1 00:00:00 bash
5014 pts/1 00:00:00 bash
5016 pts/1 00:00:00 sleep
5015 pts/1 00:00:00 sleep
5013 pts/1 00:00:00 ps
ネストされた中括弧が見えるように| ps -H
をミックスから除外して、別のシェルでps auxf | less
を実行できます。
saml 29190 0.0 0.0 117056 3004 pts/1 Ss 13:39 0:00 \_ bash
saml 5191 0.0 0.0 117056 2336 pts/1 S+ 14:42 0:00 | \_ bash
saml 5193 0.0 0.0 107892 512 pts/1 S+ 14:42 0:00 | | \_ sleep 20
saml 5192 0.0 0.0 107892 508 pts/1 S+ 14:42 0:00 | \_ sleep 20
saml 5068 0.2 0.0 116824 3416 pts/6 Ss 14:42 0:00 \_ bash
saml 5195 0.0 0.0 115020 1272 pts/6 R+ 14:42 0:00 \_ ps auxf
saml 5196 0.0 0.0 110244 880 pts/6 S+ 14:42 0:00 \_ less
ただし、パイプを取り出してこの形式のコマンドを使用すると、実際に期待することがわかります。
$ { { { sleep 10; } ; { sleep 10; } ; sleep 10; } } | watch "ps -H"
結果のウォッチウィンドウで、2秒ごとに更新が行われます。
これが最初のsleep 10
です:
PID TTY TIME CMD
29190 pts/1 00:00:00 bash
5676 pts/1 00:00:00 bash
5678 pts/1 00:00:00 sleep
5677 pts/1 00:00:00 watch
5681 pts/1 00:00:00 watch
5682 pts/1 00:00:00 ps
これが2番目のsleep 10
です。
PID TTY TIME CMD
29190 pts/1 00:00:00 bash
5676 pts/1 00:00:00 bash
5691 pts/1 00:00:00 sleep
5677 pts/1 00:00:00 watch
5694 pts/1 00:00:00 watch
5695 pts/1 00:00:00 ps
これが3番目のsleep 10
です。
PID TTY TIME CMD
29190 pts/1 00:00:00 bash
5676 pts/1 00:00:00 bash
5704 pts/1 00:00:00 sleep
5677 pts/1 00:00:00 watch
5710 pts/1 00:00:00 watch
5711 pts/1 00:00:00 ps
中括弧の異なるネストレベルで呼び出された3つすべてのスリープは、実際にはBashのPID 5676内に留まっていることに注意してください。ですから、あなたの問題は| ps -H
の使用によって自己に負わされていると思います。
| ps -H
(パイプなど)を使用すると、追加のサブシェルが発生するので、何が起こっているのかを調べるときにこのメソッドを使用しないでください。
テストの結果を投稿します。これにより、bashがグループコマンドのサブシェルを作成し、パイプラインの一部である場合はonlyであると結論付けます。これは、サブシェルでも呼び出されるいくつかの関数。
$ { A=1; { A=2; sleep 2; } ; echo $A; }
2
$ { A=1; { A=2; sleep 2; } | sleep 1; echo $A; }
1