リダイレクトおよび/またはパイプを含む組み込みコマンドの動作のPOSIX仕様は何ですか?

リダイレクトおよび/またはパイプを含む組み込みコマンドの動作のPOSIX仕様は何ですか?

次のコマンドを検討してください。

exit > /dev/null
exit | cat

一部のシェル(ksh、bash、(d)ash)では、動作は同じです。最初のコマンドを実行すると、シェルはすぐに終了しますが、2番目のコマンドには目立つ動作はありません。

exit私は最初のコマンドがfork(2)を含まないのに対して、2番目のコマンドは2つ(1つはexec用、もう1つはexecve(2)用)に関連しているという結論に達しましたcat

POSIX仕様のセクション2.14を見てみましたが、それを明示的に説明する項目が見つかりませんでした。

POSIXでは、一部のコマンドはfork(2)を実行しないでくださいが、他のコマンドは必ず実行する必要があると指定していますか?最初のコマンドのサブシェルを作成することは標準に従って許可されていますか?


( exit )角かっこはexit実際にコマンドを実行するサブシェルを生成するので、現在シェルを終了してはいけないことを知っています。つまり、サブシェルがすぐに終了することを意味します。しかし、ここには角かっこがないので、リダイレクトとパイプについてはよくわかりません。

私がこの質問をしたのは、最近のコースの練習で最小限のUnixシェルを実装するように指示されたからです。これを補うために実装できる「オプションの機能」がたくさんあります。これらの「オプションの機能」の1つは、リダイレクトとパイプの組み合わせです。私たちはいくつかの異なる実装を持っており、そのうちのいくつかは他のものよりもPO​​SIXの指定された動作に近いと思うので、実際の動作がどのように指定されるかを知りたいと思います。

答え1

素晴らしい(出口)、

これ出口ユーティリティはシェルを有効にする必要があります。やめる 現在の実行環境[...]

そして(2.12。シェル実行環境)

サブシェル環境は、シェル環境のコピーとして作成する必要があります。 [...] さらに、各コマンドについて サブシェル環境にあるマルチコマンドパイプライン;ただし、拡張機能を使用すると、パイプラインの一部またはすべてのコマンドを現在の環境で実行できます。他のすべてのコマンドは 現在のシェル環境

したがってexit、パイプラインの内容は独自の実行環境/サブシェルで実行および終了しますが、単純なコマンドの内容はデフォルトのシェルexit > /dev/null環境で実行されます。 (コメントで指摘したように、リダイレクトは実際にはまったく影響しません。)

2番目の引用符の間の部分は、一部のシェルがデフォルト環境にパイプされたすべてのコマンドを実行できることを意味するため、この場合でもシェル全体が終了します。実際には、パイプラインの最後のコマンドに対してこれを行うのがより一般的です。

たとえば、Bashではlastpipe次のようになります。

$ bash -c 'true | exit; echo end.'
end.

しかし、

$ bash -O lastpipe -c 'true | exit; echo end.'

何も印刷されません。

答え2

これはexitfor のサブシェルではなく for で実行されるために発生します。 exit | catexit > /dev/nullサブシェルを終了してもメインシェルは終了しません。:

現在の実行環境がサブシェル環境の場合、シェルは指定された終了状態でサブシェル環境を終了し、サブシェル環境を呼び出した環境で実行され続けます。

ただし、サブシェルとサブシェル以外の具体的な違いについては、この章で詳しく説明します。POSIX 標準 2.12- 関連する節全体を引用してください。

サブシェル環境はシェル環境のコピーとして作成する必要がありますが、無視されない信号トラップはデフォルトの動作に設定する必要があります。サブシェル環境に対する変更は、シェル環境に影響を与えてはいけません。コマンドの置換、括弧でグループ化されたコマンド、および非同期のリストは、サブシェル環境で実行する必要があります。さらに、マルチコマンドパイプラインの各コマンドはサブシェル環境にありますが、拡張機能によってパイプラインの一部またはすべてのコマンドを現在の環境で実行できます。他のすべてのコマンドは、現在のシェル環境内で実行する必要があります。

これはexit | cat説明に適しています。

マルチコマンドパイプラインの各コマンドはサブシェル環境にあります。

したがって、通常サブシェルで実行されます。ただし、これは問題を引き起こす可能性があります。

ただし、拡張機能を使用すると、パイプラインの一部またはすべてのコマンドを現在の環境で実行できます。

...これは、すべてのシェルでこれが保証されるわけではないことを意味します。以前は、ある実装は現在のシェルでパイプの右側を実行し、次のコードはKSHの一部の実装では機能しますが、他の実装では機能しないコードをデバッグする必要がありました。

cat foo | while read line
do
    X="$line"
done

だからいつもパイプを想定してください可能サブシェルを作成しますが、それに依存しないでください。

関連情報