私の本では(SobelLinux実用ガイド,4e)テキストは以下の通りである。
角かっこ制御演算子を使用してコマンドをグループ化できます。この手法では、シェルはグループごとにサブシェルと呼ばれる独自のコピーを作成します。各コマンドセットをリストとして処理し、各コマンドを実行するための新しいプロセスを作成します。
誤解したくないのでここに質問をするようになりました。サブシェルを作成するかどうか〜しなければならないこれらの()グループコマンドを使用する必要がありますか、それとも同じサブセールスで特定のコマンドを実行する方法ですか?
たとえば、見てみましょう。コマンド(で実行可能PATH
)a
とがあるとしますb
。コマンドプロンプトで次のように入力するとどうなりますか?
a ; b
(a ; b)
(a) ; (b)
答え1
a
平等foo=xyz
でb
平等にしようecho $foo
。あるいは、むしろ関数として定義してみましょう。
a() { foo=xyz; }
b() { echo $foo; }
次に、表示される各バリエーションを試してみましょう。それぞれの場合、foo
最初に初期化し、abc
最後に値を印刷します。foo
右出力:
foo=abc; a ; b; echo $foo
=>xyz
、xyz
foo=abc; (a ; b); echo $foo
=>xyz
、abc
foo=abc; (a) ; (b); echo $foo
=>abc
、abc
したがって、最初の割り当ては基本レベルで行われ、残りのスクリプトで見ることができます。 (これらの関数は{ ..; }
グループ化構成を使用しているため、デフォルトのシェルで実行されます。)2番目の場合、最初の印刷出力と同じサブシェルで割り当てが行われますが、残りのスクリプトには影響しません。 3番目では、割り当ては最初のサブシェルで行われ、後でスクリプトではなくそのサブシェルでのみ表示されます。
その後、再び実行可能ファイルについて尋ねましたが、PATH
とにかくシェルの実行環境に影響を与えないため、サブシェルで実行されるかどうかは重要ではありません。つまり、ls
と同じです(ls)
。しかし、シェル組み込みの場合、違いは重要です。たとえば、read
(変数の設定)またはexit
((サブ)シェルの終了)を考えます.
コマンド置換もサブシェルで実行されます。例えば
foo=abc
echo $(foo=xyz; echo $foo)
echo $foo
印刷xyz
してくださいabc
。
もちろん、コマンド置換構文も括弧を使用しているため、一定の対称性があります。 (もしそうなら、(( ... ))
それはまったく異なる問題です。)
シェルと非同期的または同時にコマンドを実行するすべての操作は、サブシェルも開始する必要があります。これを行うには、デフォルトのシェルプロセスを変更できない新しいプロセスを作成する必要があるためです。
一般的なケースはパイプです。foo | bar | doo
と両方がサブシェルで実行され、foo
サブシェルまたはデフォルトのシェル環境で実行できます。bar
doo
例えば
foo=abc
{ foo=xyz; echo $foo; } | cat
echo $foo
印刷xyz
、abc
。
望むより:私の変数が1つの「読み込み中」ループではローカルですが、一見似ている他のループではローカルではないのはなぜですか?
foo &
明らかに、<( foo )
プロセス置換()または他の同様の手段を使用してバックグラウンドで明示的に何かを実行することもサブシェルを開始します。
とにかく( .. )
サブシェルを起動するには、明示的にサブシェルを起動する必要があります。他の人にとっては、それが副作用であると言うかもしれません。
答え2
これらの3行は異なることをします。
[me@here foo]$ echo A-$BASHPID ; echo B-$BASHPID
A-534171
B-534171
[me@here foo]$ (echo A-$BASHPID ; echo B-$BASHPID)
A-534798
B-534798
[me@here foo]$ (echo A-$BASHPID) ; (echo B-$BASHPID)
A-534808
B-534809
まず、現在のシェルのコンテキストでaを実行してからbを実行します(この例ではpid = 534171)。 2番目は新しいサブシェル(pid = 534798)を作成し、その新しいサブシェルでaを実行してbを実行します。 3番目は新しいサブシェル(pid = 534808)を作成し、その中で実行します。サブシェルが終了したら、別のサブシェル(pid = 534809)を作成し、そのサブシェルでbを実行します。これらのサブシェルは通常、元のシェルから環境全体を継承せず、エクスポートされていないシェル変数、ファイル記述子、 'ERR、DEBUG、RETURNトラップ処理は特別な場合です)、サブシェル環境の一部は明示的に変更されます(プロセスIDS、シェル履歴...)。子シェルで実行されるコマンドは通常、親シェル環境を変更できません。
[me@here foo]$ A=foo
[me@here foo]$ A=bar
[me@here foo]$ (A=baz)
[me@here foo]$ echo $A
bar
バイナリ実行可能ファイルもこれらのサブシェルから始まります。できる違いを見つけて、別の方法で行動してください。