while内でsshを使うと1回で止まる

ファイルを1行ずつ読みsshで処理する内容のLinuxシェルで、期待通りに動作しない問題が発生しました。最初の1行は処理をするのですが、2行目以降は処理をしてくれません。

余計な部分を排除するとこんな感じです。
while read line
do
	echo ${line}
	ssh ${remotehost} "echo ${line}"
done < ${listfile}

sshの行がなければきちんとループします。よってループの書き方は悪くないハズです…。
ループをせずにsshコマンドを順次実行するシェルに書き換え実行したところ、これもうまくいきました。なので、sshの行も悪くないハズです。
line=…
ssh ${remotehost} "echo ${line}"
line=…
ssh ${remotehost} "echo ${line}"

ファイル読込の方法を、少し異なる記述に変えてみた。しかしこれもダメ…。
cat ${listfile} | while read line
do
	echo ${line}
	ssh ${remotehost} "echo ${line}"
done

デバッグモードで動作させると、sshコマンドを処理した後、readを行ったうえでループを抜けていることが分かりました。
+ read line
+ echo …
+ ssh …
+ read line

つまり「ファイルをもう読めない」と判断され「正しく」ループが終了しているようです。改行文字がおかしいのかとか、何らかの理由でファイル内のポインタが末尾に飛んでしまうのかとかいろいろ可能性を考えましたがよくわかりませんでした。

次に「while read」ではなく「for」でループするような記述も試してみました。するとこれについては期待通りに動きました!つまり、sshの前にファイルの内容をすべて展開してしまえばOKだと。
for line in `cat ${list}`
do
	echo ${line}
	ssh ${remotehost} "echo ${line}"
done

「while read」と「ssh」の組み合わせで発生する特殊な現象なのかと考え、この3つの単語を検索キーとしてググると、あっさりたくさんの情報が得られました。またヘルプを参照したりさらに実験を行なうことにより原因が分かりました。(ググった先のサイトでは明らかに間違っていそうな情報があったり、またどれを読んでも私がすっきるする説明は見つかりませんでした…)

原因はsshコマンド実行に伴う標準入力の切替です。sshコマンドを実行すると、ローカルホストからの標準入力を停止し、sshで指定したリモートホストからの標準入力の受付を開始します。

つまり、上記のコードの例では、ローカルホストのファイルの読込みを終了させた上でsshコマンドを実行し、再びreadコマンドを実行しようとしているわけです。この時、既にファイルが閉じている為にwhileが終了してしまうわけです。

標準入力が切り替わってしまう対策としては、sshにnオプションをつければOK。nオプションによって、sshコマンドでの標準入力を禁止することが出来ます。しかし、ssh先で標準入力を行なう処理をしたい場合にはこの対策は使えません…。
while read line
do
	echo ${line}
	ssh -n ${remotehost} "echo ${line}"
done < ${listfile}


対策は2つです
  1. 標準入力を使わずにループする
  2. ssh先で標準入力使わないようにする(sshにnオプションをつけて実行する)
※ググると2.しか出てこなかったのですが、ケースにより1.もかなり有力な対策だと思います。