[bash]サブシェルがパイプで受け取った標準入力の扱いとcdコマンドの動作

こんにちは、hachi8833です。

bashのサブシェルで標準入力を受け取った場合の扱いについて社内で行われたやりとりをメモします。

検証にはDocker上で動くUbuntu Linux(4.4.0-47-generic)を使いました。

質問

bashのサブシェルについての疑問です。以下のコマンドがあるとします。

$ echo "aiueo" | (cd /home/hoge && cat - )
  1. サブシェル(上の()で囲まれた部分)はパイプで受け取った標準入力をどう扱うか
  2. cdは標準入力をどう扱うか
  3. 実行してみると以下のようにcatまで標準入力が届いているように見えるが、cdは何が行われていたのか

question

回答

1. サブシェルはパイプで受け取った標準入力をどう扱うか

標準入力の扱いについては、サブシェルであってもなくても同じです。以下のように、パイプのつなぎ先がサブシェルであってもなくても出力は同じです。

ans 1

2. cdは標準入力をどう扱うか

cdコマンドに標準入力を渡すと何も行われず(ディレクトリも変わりません)、何も出力せずに終了します。

ans 2

  • 上: パイプで標準入力を渡すと、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 -)

cat'n'cat

再回答

$ echo "a" | (cat - && cat -)
  • 1番目のcat -の段階で標準入力は空っぽになります。
    • もし1番目のcat -cdだったら、cdは標準入力の情報にまったく触らないので、そのまま残り続けます(標準入力を受け取りません)。
  • 2番目のcat -は、空っぽの標準入力を受け取るので何も出力しません。

参考までに、以下のようにすれば2番目のcatで出力されます。

$ echo "a" | (cat - | cat -)

以下の実行例では、2番目のcatが機能していることを確認するために間にsedをはさんでいます。

second cat

関連記事

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833 コボラー、ITコンサル、ローカライズ業界を経てなぜかWeb開発者志願。 これまでにRuby on Rails チュートリアルの大半、Railsガイドのほぼすべてを翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

Rubyスタイルガイドを読む

BigBinary記事より

ActiveSupport探訪シリーズ