問題
シェル(ただし Bourne Shell 系に限る)はお友達です。 一見すると役に立たないように思えるコマンドでも、 組み合わせ次第で複雑な処理をこなすための道具になります。
例えば行毎に ID が記載されているファイル ids があったとしましょう。
各 ID を SHA1 ハッシュ値に置き換えたものが必要な場合、
以下のコマンドで生成することができます
(SHA1 ハッシュ値の算出には shasum を使っています):
cat ids | while read id
do
echo -n "$id" | shasum
done | cut -d ' ' -f 1 >ids.sha1
このように、行単位で何か処理を行う場合には read を使います。
ちょっとしたことなら sed や awk で済ませられるのですが、
上記のように「行毎にコマンドを実行して云々」をやろうとすると無理が出てくるので、
read を使う方が自然です
(若者ならば何であれ perl で済ませるところですが、
今回は read を使いたいので考えないことにします)。
ところで、いきなり上記のようなコマンド列がすらすら書ける人は稀です。 ふつうの人は単純な処理(例えば指定した ID から SHA1 ハッシュ値を生成するだけ)を実現してから、 徐々に目的の処理を実装していくことになります。
筆者の場合、 read は滅多に使ったことがないため、
まずおさらいがてら read の使い方の確認から始めました。
read は標準入力から1行読み込み、
読み込んだ文字列を引数で指定された名前の変数に格納します
(話を単純にするため「単語」分割云々は考えないことにします)。
ということは、以下のコマンドを実行すると i には 2 が格納されるので、
1 2 の順で出力が行われるはずです。試してみましょう:
$ i='1'; echo "$i"; echo '2' | read i; echo "$i"
1
1
えっ......
3回目の echo では 2 が出力されると予想したのですが、
何故か 1 が出力されました。一体 read が格納した文字列はどこへ消えたのでしょうか。
さらに不可思議なことに、以下のように while で括ると今度は期待通りの出力がなされます:
$ i='1'; echo "$i"; echo '2' | while read i; do echo "$i"; done
1
2
まさかと思って while の後に i の内容を出力してみると、今度は以下の結果になります:
$ i='1'; echo "$i"; echo '2' | while read i; do echo "$i"; done; echo "$i"
1
2
1
read の挙動を考えると、
while の有無(と前後)で i 内容がころころ変動するのは不可思議です。
これはどういうことなのでしょうか。

