やりたいこと

シェルスクリプトで、受け取ったデータについて一行ずつ何らかの処理をしたくなりました。

典型的なやり方

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 -  
Tags: qiita_mirror