シェルのループ内で標準入力を受け付けるコマンドを書くと?

Togetterではじめてまとめを作ってみました。 シェルのwhileループ内に標準入力から待ち受けるコマンドを書いた人から、質問を受けました。が・・・不覚にも解説できませんでした。 Twitter上で助けを求めたところ、リプライをいただきありがとうございます。 その後バッファリングに関する事など、更に高度な場合の問題や解説も見つけました。
8
ぱぴろんちゃん😱🙀 @papiron

#シェル芸 これは一体何が起きているのか?解説出来る方求む・・・ $ seq 1 10 | while read n; do awk '{print $1}'; done 2 3 4 5 6 7 8 9 10

2017-12-15 19:09:32
のぎろ @nogiro_iota

@papiron 1がreadで読まれてからループに入って、残りがawkで読まれてるのではないかと

2017-12-15 19:12:02
ぱぴろんちゃん😱🙀 @papiron

@nogiro_iota set -xしてみたら、確かにそのような動きをしてるみたいですね。

2017-12-15 23:19:13
ぱぴろんちゃん😱🙀 @papiron

@nogiro_iota whileループ内に標準入力から入力待ちになるコマンドを書くとかやった事は無かったので、正直この動きは分かりにくいです・・・

2017-12-15 23:35:09
ginjiro @gin_135

@papiron 試しに $ seq 1 10 | while read n; do sed -n p; done をやってみましたが、この場合でも1は出力されませんでした… awkやsed側の問題と言うより、while構文の動作で何かが起きている感じですね

2017-12-15 19:24:20
ぱぴろんちゃん😱🙀 @papiron

@gin_135 他の方の指摘にもありましたが、whileでの読み込みは1回だけで後はsedが入力を食ってしまってるみたいですね。これ分かりにくいですわ・・・set -xしてみたら何となく分かりました。

2017-12-15 23:32:25
eban @eban

いやつまりこういうことですよ seq 10 | ( read; cat ) #シェル芸

2017-12-15 19:26:48
eban @eban

seq 10 | (ruby -e '$stdin.sysread(2)'; cat) #シェル芸

2017-12-16 14:10:43
eban @eban

% seq 10 | (head -1; cat) でcatされないのはheadでバッファリングされるのでそこで全部読み込まれてしまうため。 2000ぐらい送れば出てくる seq 2000 | (head -1; wc -l) #シェル芸

2017-12-16 15:43:36
eban @eban

bashとかのreadはバッファリングしてないのであれで1行だけ読み捨てられる。 つまりwhile readはとても遅い。 #シェル芸

2017-12-16 16:00:49
eban @eban

-cならバッファリングされない seq 10 | (head -c2; wc -l) #シェル芸

2017-12-16 16:08:37
ゾンビプログラマ @qeMkDrlaJwLLcQ

@papiron 例えばこんな事をしてみたり $ df -h | { read h; echo "$h"; sort -k3,3hr; }

2017-12-15 19:25:35
ぱぴろんちゃん😱🙀 @papiron

@Heliac1999 あああ、ヘッダ行以外を並べ替えてるんですね・・・なるほど・・

2017-12-15 23:18:29
Metaphor @metaphoricwords

1はreadが読んで、2から10はawkが読んで、もう全部読んだのでwhileが終了したということかな twitter.com/papiron/status…

2017-12-15 19:36:04
Metaphor @metaphoricwords

$ seq 1 10 | while read n; do echo "$n foo"; awk '{print $1}'; echo "$n bar"; done 1 foo 2 3 4 5 6 7 8 9 10 1 bar あってたっぽい

2017-12-15 19:42:06
ぱぴろんちゃん😱🙀 @papiron

@metaphoricwords ああああなるほど・・・こういうことですね!

2017-12-15 23:48:01
ゆんくん @ynkn_yunkun

@papiron bashでは同一サブプロセスではクローズされていない標準入力が次のコマンドに引き継がれるので、readで1行だけ読み(そして変数nは放置されているので出ず)、後のawkが続きすべて読んで2以降をprintしていると思います。seq 1 100 | (head;tail) なんかと同パターンですね。

2017-12-15 22:12:31
ぱぴろんちゃん😱🙀 @papiron

@u1nakano ぉぉぉ・・・おおお??readで読んでるのは1行だけということですか・・・

2017-12-15 23:16:49
ゆんくん @ynkn_yunkun

@papiron ええ、awkさんが食いしん坊なんですねえ。。

2017-12-15 23:28:38
ふみやす@元シェルまおう(自称でない) @satoh_fumiyasu

問題です。以下は何故こうなるでしょうか? #シェル芸 $ seq 10 |(sed '3q'; grep 9) 1 2 3 $ seq 10 |(read v; echo $v; read v; echo $v; read v; echo $v; grep 9) 1 2 3 9 $ seq 10 >seq-10.txt $ cat seq-10.txt |(sed '3q'; grep 9) 1 2 3 (sed '3q'; grep 9) <seq-10.txt 1 2 3 9 twitter.com/papiron/status…

2017-12-23 20:28:37
ぱぴろんちゃん😱🙀 @papiron

「シェルのループ内で標準入力を受け付けるコマンドを書くと?」をトゥギャりました。 togetter.com/li/1182814

2017-12-23 20:10:07
ふみやす@元シェルまおう(自称でない) @satoh_fumiyasu

Solaris 10 だとこうなるわー。#シェル芸 $ (/bin/sed '3q'; grep 9) <seq-10.txt 1 2 3 $ (/usr/xpg4/bin/sed '3q'; grep 9) <seq-10.txt 1 2 3 9

2017-12-23 20:31:44