• ベストアンサー

/dev/stdinはそれ以降の入力をかき消す???

意味不明なタイトルですみません。 今回は/dev/stdinについて教えてください。 /dev中のファイルは特殊ファイルなので深追いしない方がいいとは思うのですが詳しい方お願いします。 次のコマンドとその出力を見てください。 $ echo "a > a > a > a > a" | while read LINE > do > grep -l str $LINE > done a a a a a $ これはgrepにファイルaを5回検索させる命令ですが この5行のaの途中に/dev/stdinを挿入すると次のように/dev/stdin以降が出力されなくなってしまうのです!! $ echo "a > /dev/stdin > a > a > a > a" | while read LINE > do > grep -l str $LINE > done a $ この/dev/stdinとはいったい何者なのですか!? このコマンドでは、いったいどのような事態が起こっているのですか!?

質問者が選んだベストアンサー

  • ベストアンサー
  • Lean
  • ベストアンサー率72% (435/603)
回答No.2

>> a" | while read LINE >> do >> grep -l str $LINE >> done grepコマンドの前に echo "grep -l str $LINE" とかを入れてみると、grepがどういう実行イメージで何回実行されるか分かると思いますよ。 上記を実施してみれば分かりますが、2回目に grep -l str /dev/stdin が実行されるのは分かりますよね? /dev/stdinが何で、上記が実行されるとどこから読み込みが行われ、その時どういう内容が読み込まれるのか理解出来れば、何故そうなるのか分かると思いますよ。 理解出来ないなら、一番の最初に書いたecho "grep -l str $LINE" を追加して、さらに >> grep -l str $LINE を cat $LINE にでも変更して実行してみれば理解出来ると思いますよ。

zyousuke
質問者

補足

Leanさん、ご返信ありがとうございます。 助言いただいたとおりに操作してみました。 まず、grepコマンドの前にecho "grep -l str $LINE"を入れてみました。 $ echo "a > /dev/stdin > a > a > a > a" | while read LINE > do > echo "grep -l str $LINE" > grep -l str $LINE > done grep -l str a a grep -l str /dev/stdin $ 1回目のループではecho、grepともに予想通りの出力です。 2回目のechoも予想通りです。 2回目のgrepは何も出力しません。 これは/dev/stdinに文字列strがないことを物語っています。 そして処理は終了します。 whileは入力行がある限り繰り返すはずなのにここで終了してしまうということは、 実はすでに最終行まで入力が終了しているということ?? これだけでは、よく分からないので、 次にgrep -l str $LINEをcat $LINEに変更してみました。 $ echo "a > /dev/stdin > a > a > a > a" | while read LINE > do > echo "grep -l str $LINE" > cat $LINE > done grep -l str a str grep -l str /dev/stdin a a a a $ 1回目のループではecho、catともに予想通りの出力です。 2回目のechoも予想通りです。 2回目のcatの出力は"a a a a"でしょうか 2回目のcatとはすなわち cat /dev/stdin であるため、 /dev/stdinの内容は"a a a a"であるといえます。 そして3回目のループは実行されない。 それは2回目のループまでで、全ての入力行が終了してしまうからということでしょうか。 整理します。 /dev/stdinとは標準入力のこと。 そして、ここでいう標準入力は"a /dev/stdin a a a a"です。 しかし/dev/stdinの内容はそれ以降に入力した"a a a a"となっていました。 そして、入力したはずの"a a a a"はかき消されていて、 かき消された内容は/dev/stdinの内容と一致する!? うーん頭が混乱してきましたが、自分なりに打ち出した見解はこうです。 /dev/stdinは標準入力の内容を保持しているが、絶え間なく更新しているため、 catで中を覗こうとしても、そのタイミングにより、様々な結果が表示される。 また、標準入力は1個しかないので、たとえ仮想的に標準入力を表す/dev/stdinを覗いたとしても、その時点で入力が完了したと判断され、元の入力はかき消されてしまう。 うまく説明できないのですが、こんな感じでしょうか。 今回の例では1回目のループ時点では/dev/stdinの内容は"a /dev/stdin a a a a"でしたが、このとき1行目のaの入力が完了したため/dev/stdinの内容が"a a a a"となり、2行目の入力は/dev/stdinなので全ての入力が一気に渡されwhile自体が終了したということかな・・・。 こう考えれば、つじつまが合う気がするのですが、いかがでしょうか。

その他の回答 (2)

  • Lean
  • ベストアンサー率72% (435/603)
回答No.3

最後に書かれた理解でいいと思います。 まず、echoによって標準入力(/dev/stdin)に a /dev/stdin a a a a が入ります。 次に「while read LINE」で1行分(「a」)標準入力(/dev/stdin)から読み込まれますので、標準入力(/dev/stdin)の内容は、 /dev/stdin a a a a になります。 次の「while read LINE」でまた1行分(「/dev/stdin」)標準入力(/dev/stdin)から読み込まれますので、標準入力(/dev/stdin)の内容は、 a a a a になります。 2回目のgrepの実行は grep -l str /dev/stdin になり、標準入力(/dev/stdin)からEOF(ファイルの最後)になるまで読み込まれます。 つまり、標準入力(/dev/stdin)に残っていた内容の a a a a が全て読み込まれ、標準入力(/dev/stdin)には何も残っていない状態になり、「while read LINE」では読み込むものがないのでwhileループが終了になります。

zyousuke
質問者

お礼

Leanさん、お返事ありがとうございます。 いただいたヒントと今までの経験と知識を総動員して解答を導き出せたのですごい嬉しいです! 特殊ファイルとは、その名のとおり、普通のファイルとは1癖も2癖も違って、扱いが難しいですね。 今回、どうやって、本件の現象を発見したかといいますと、 今、指定ディレクトリー中の全ファイルをgrep検索するという、結構使えそうなシェルスクリプトを作成中でして、 これを例のごとくルートディレクトリーでテストしたところ/dev/stdin以降の結果が意図したものと全然ちがっていたのです。 これで原因が分かりました。 findで取得したファイル名をパイプを通してgrepに渡しているのですが、 このファイル名に/dev/stdinが含まれていた場合、このスクリプトの結果はめちゃくちゃになることが分かりました。 今考えられる修正案は、findで取得したファイル名の一覧に/dev/stdinが含まれていたら、これを取り除いてからgrepへ渡すということぐらいですが、 これは、あんまりかっこよくないので、もう少しいい回避策を考えてみます。

  • mtfoggy
  • ベストアンサー率14% (37/255)
回答No.1

>この/dev/stdinとはいったい何者なのですか!? 標準入力 ぐぐれば、すぐわかるはずですが。

zyousuke
質問者

お礼

mtfoggyさん、アドバイスありがとうございます。 /dev/stdinというファイルが何者かということについては、 すぐにとはいかないまでも、頑張ってぐぐれば自力で回答を導き出せる疑問でした。 またstdinという名前から自分でも恐らく標準入力を表すファイルなのだろうと予想していたのでした。 今回は質問文中の2個目の命令行のような見慣れない出力を発見したので、 これを皆さんに見てもらいたいと思い、ここへ質問に参ったというわけです。 その際ついでに/dev/stdinとは何かという容易な質問もしてしまったわけですが許してください~w

関連するQ&A