最初のスクリプト:
$ { mkdir dir; cd dir; pwd; } | cat; pwd;
./dir
.
2 番目のスクリプト:
$ { mkdir dir; cd dir; pwd; }; pwd;
./dir
./dir
| catこれが現在のディレクトリに影響を与えるのはなぜですか? そして、それを解決する方法は?最初のスクリプトが 2 番目のスクリプトとまったく同じように機能する必要があります。cat現在のディレクトリを に戻したくありません.。
実行すると:
{ mkdir -p dir; cd dir; pwd; } | cat; pwd
また
{ mkdir -p dir; cd dir; pwd; } | date
また
{ mkdir -p dir; cd dir; pwd; } | ls
サブシェルのパイプの LHS でコマンドのグループを実行しているため、両方のコマンド (LHS と RHS) が完了した後change dir 、現在のシェルには反映されません。
ただし、実行すると:
{ mkdir -p dir; cd dir; pwd; }; pwd;
間にパイプがないため、中括弧内と中括弧pwd外のすべてのコマンドが現在のシェル自体で実行されるため、ディレクトリが変更されます。
PS: 次の行にも注意してください。
( mkdir -p dir; cd dir; pwd; )
角括弧内のコマンドはサブシェルで実行されるのに対し、中括弧はグループ化にのみ使用されるため、現在のシェルの現在のディレクトリも変更しません。
変数に関するパイプライン コマンドの動作は実装定義です。
たまたま、bashすべてのコンポーネントをバックグラウンド シェルに配置するために which choose を使用cd して、メイン シェルで効果が失われるようにします。
ksh現在のシェルでパイプラインの最後の要素を保持することを選択して実行した場合、およびcd最後のステートメントに入れた場合、動作は期待したものになります。
$ bash
$ { mkdir -p dir; cd dir; pwd; } | { cat; mkdir -p dir; cd dir; pwd ; } ; pwd;
/tmp/dir
/tmp/dir
/tmp
$ ksh
$ { mkdir -p dir; cd dir; pwd; } | { cat; mkdir -p dir; cd dir; pwd ; } ; pwd;
/tmp/dir
/tmp/dir
/tmp/dir
パイプは、現在の作業ディレクトリをリセットしません。パイプは、パイプの両側に 1 つずつ、bash でサブシェルを作成します。また、サブシェルは現在の作業ディレクトリをリセットしません。サブシェルには、環境自体への変更が含まれており、親シェルには反映されません。
最初のスクリプトで:
$ { mkdir dir; cd dir; pwd; } | cat; pwd;
はcd、パイプの左側のサブシェル内で実行されます。作業ディレクトリは、そのサブシェルでのみ変更されます。親シェルの作業ディレクトリは変更されません。1 つ目pwdは と同じサブシェルで実行されるcdため、変更された作業ディレクトリが読み取られます。最後のpwdは親シェルで実行されるため、変更されていない親シェルの作業ディレクトリを読み取ります。
2 番目のスクリプトで:
$ { mkdir dir; cd dir; pwd; }; pwd;
サブシェルは作成されません。中括弧はサブシェルを作成しません。はcd親シェルで実行され、両方ともpwd変更された作業ディレクトリを読み取ります。
詳細と説明については、次の詳細な bash マニュアルを参照してください。
マニュアルには次のように書かれていますが:
{ list; }
Placing a list of commands between curly braces causes the list to be executed in the current shell context. No subshell is created.
パイプラインに配置しても、サブシェルに配置されます。
{ list; } | something
以来
Each command in a pipeline is executed in its own subshell (see Command Execution Environment).
コマンド{ }自体はサブシェルに配置されませんが、それ自体のより高いコンテキストは同じである理由です。
実行中のテストとして、まったく同じになります( ):{ }
# echo "$BASHPID"
6582
# ( ps -p "$BASHPID" -o ppid= ) | cat
6582
# { ps -p "$BASHPID" -o ppid=; } | cat
6582
どちらも親プロセスを呼び出し側シェルとして送信します。