こんにちは、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
をはさんでいます。