こんにちは、hachi8833です。
bashのサブシェルで標準入力を受け取った場合の扱いについて社内で行われたやりとりをメモします。
検証にはDocker上で動くUbuntu Linux(4.4.0-47-generic)を使いました。
質問
bashのサブシェルについての疑問です。以下のコマンドがあるとします。
$ echo "aiueo" | (cd /home/hoge && cat - )
- サブシェル(上の
(と)で囲まれた部分)はパイプで受け取った標準入力をどう扱うか cdは標準入力をどう扱うか- 実行してみると以下のように
catまで標準入力が届いているように見えるが、cdは何が行われていたのか
回答
1. サブシェルはパイプで受け取った標準入力をどう扱うか
標準入力の扱いについては、サブシェルであってもなくても同じです。以下のように、パイプのつなぎ先がサブシェルであってもなくても出力は同じです。
2. cdは標準入力をどう扱うか
cdコマンドに標準入力を渡すと何も行われず(ディレクトリも変わりません)、何も出力せずに終了します。
- 上: パイプで標準入力を渡すと、
cdは何も出力しないので、echoも出力されません。 - 下: セミコロンで区切った場合、
echoの出力とcdはそれぞれ行われます。
注意: cdはシェルコマンドなので、cdを実行してもプロセスは起動しません。
3. catまで標準入力が届いているように見えるが、cdは何が行われていたのか
サブシェル内でcdすると、サブシェル内のカレントディレクトリが変更されます。
サブシェル内でカレントディレクトリを変更すると、サブシェルが別シェルとして起動します。たとえば、cd /homeだとカレントディレクトリは変更されますが、(cd /home)ならカレントディレクトリは変更されません。
サブシェルを利用すると、メインのシェルのカレントディレクトリや環境変数に影響せずにcdや環境変数設定を行えます。
例
サブシェルのサンプルスクリプトです。
$ echo "aiueo" | ( cd /tmp && cat - > aiueo.txt)
上のスクリプトはサブシェルで/tmpにaiueo.txtを作成し、実行後は元のディレクトリに戻ります。
再質問
なるほど、サブシェルはあんまり関係ないんですね。cdが標準入力を受け取っても何もしないだろうというところまでは理解していました。
腑に落ちなかったのは、「それならcatには何も届かないはずではないか?」という点です。たとえば以下を実行すると"a"は一度しか表示されません。質問3.はそこについてお聞きしたかったのでした。
$ echo "a" | (cat - && cat -)
再回答
$ echo "a" | (cat - && cat -)
- 1番目の
cat -の段階で標準入力は空っぽになります。- もし1番目の
cat -がcdだったら、cdは標準入力の情報にまったく触らないので、そのまま残り続けます(標準入力を受け取りません)。
- もし1番目の
- 2番目の
cat -は、空っぽの標準入力を受け取るので何も出力しません。
参考までに、以下のようにすれば2番目のcatで出力されます。
$ echo "a" | (cat - | cat -)
以下の実行例では、2番目のcatが機能していることを確認するために間にsedをはさんでいます。





