やりたいこと
シェルスクリプトで、受け取ったデータについて一行ずつ何らかの処理をしたくなりました。
典型的なやり方
while に標準入力を流し込んで、 read コマンドで一行ずつ受け取れます。
printf "a\nb\nc\n" | while read line
do
echo data: $line
done
無事、こんな出力が得られるはずです。
data: a
data: b
data: c
処理を増やしたくなりました。例えば cat を差し込みます。
printf "a\nb\nc\n" | while read line
do
echo data: $line
cat
done
きっと想像がつくと思いますが、このループはあまり思ったとおりには動きません。一度しか回らないです。
data: a
b
c
おそらくこんな形で cat をわざわざ挟む人はいないと思います。
しかし、他にも同じような挙動が発生するコマンドは色々ありそうです。
例えば気付かず使いそうになるかもしれないコマンドとして、 ssh
があります。
printf "a\nb\nc\n" | while read line
do
echo data: $line
ssh sakura "echo from sakura: $line"
done
ssh 先で3回コマンドを実行したいと思っていたのですが、一度しか実行されませんでした。
data: a
from sakura: a
どうしよう?
データのサイズがあまり大きくないのであれば、 for を使ってしまうのが一つの手です。
for line in a b c
do
echo data: $line
ssh sakura "echo from sakura: $line"
done
data: a
from sakura: a
data: b
from sakura: b
data: c
from sakura: c
しかし場合によっては、どうしても標準入力から一行単位ごとにループしたい、あるいは入力サイズがでかすぎてループ前に全部展開したくないというような場合もあるかもしれません。
そういう場合は、標準入力を陽に潰してやることもできます。
printf "a\nb\nc\n" | while read line
do
echo data: $line
ssh sakura "echo from sakura: $line" </dev/null
done
data: a
from sakura: a
data: b
from sakura: b
data: c
from sakura: c
思った通りの出力が得られました。
おまけ
ssh の標準入出力のやり取りは意外と便利です。
1ファイルを転送するくらいなら、 ssh と cat だけでなんとなくできてしまいます(この例だと SCP を使わない理由があまりありませんが…)。
cat a.png | ssh sakura "cat >a.png"
ディレクトリの転送も頑張ればできます。
tar -zcf - docs/ | ssh sakura tar -zxf -