読み取り行ループ中のシェルスクリプトが最初の行の後で停止する


107

次のシェルスクリプトがあります。目的は、ターゲットファイルの各行(パスはスクリプトへの入力パラメーターです)をループして、各行に対して処理を行うことです。現在、ターゲットファイルの最初の行でのみ機能し、その行が処理された後に停止します。スクリプトに問題はありますか?

#!/bin/bash
# SCRIPT: do.sh
# PURPOSE: loop thru the targets 

FILENAME=$1
count=0

echo "proceed with $FILENAME"

while read LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh $LINE
done < $FILENAME

echo "\ntotal $count targets"

ではdo_work.sh、いくつかのsshコマンドを実行します。


1
スクリプトは問題ありませんが、do_work.shに問題がある可能性があります
sleepsort

2
はい、それはすべての入力を使い果たす可能性があります、またはsource単に終了またはとして呼び出される可能性がありますexec。しかし、このコードは本物に見えません、OPはエコーが-eラインフィードを正しく表示するために必要であることを
認識し

3
do_work.sh実行ssh万が一?
dogbane

1
はい、do_work.shは2つのsshコマンドを実行します。何か特別なことはありますか?
bcbishop

1
do_work.shソースを表示し、デバッグのために実行do.shすることをお勧めしますset -x
koola

回答:


178

問題は、あるdo_work.sh実行のsshコマンドと、デフォルトではssh、あなたの入力ファイルである標準入力から読み込みます。その結果、sshファイルの残りの部分が消費され、whileループが終了するため、処理されたのは最初の行だけです。

これを防ぐ-nには、sshコマンドにオプションを渡して/dev/null、標準入力の代わりにそれを読み取るようにします。


1
非常に便利で、このzshワンライナーを実行するのに役立ちました。ホストの読み取り中。ssh $ host do_somethingを実行します。行って
ラット

3
@ratあなたはまだ役に立たないのcatを避けたいです特にげっ歯類はこれに警戒すると思います。
Tripleee

while read host ; do $host do_something ; done < /etc/hostsそれを避けるでしょう。とても助かります、ありがとう!
ラット

httpieは、デフォルトでSTDINを読み取る別のコマンドであり、bashまたはfishループ内で呼び出されたときに同じ動作の影響を受けます。上記のようにhttp --ignore-stdin標準入力を使用または設定/dev/nullします。
ラマン

12

より一般的には、固有ではない回避策はsshwhileループの入力を消費する可能性のあるコマンドの標準入力をリダイレクトすることです。

while read -r LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh "$LINE" </dev/null
done < "$FILENAME"

の追加が</dev/nullここでの重要なポイントです(ただし、引用を修正することもいくらか重要です。シェル変数を引用符で囲むタイミングを参照してください)。read -rなしで得られるレガシーで少し奇妙な動作を特に必要としない限り、使用する必要があります-r

やや固有のソートの別の回避策はsshsshコマンドを変更するなどして、コマンドの標準入力を拘束することです。

ssh otherhost some commands here

代わりに、ヒアドキュメントからコマンドを読み取ると、便利な(この特定のシナリオでは)sshコマンドの標準入力が拘束されます。

ssh otherhost <<'____HERE'
    some commands here
____HERE

5

ssh -nオプションは、出力を別のプログラムにパイプするときにHEREdocを使用するときにsshの終了ステータスをチェックしないようにします。そのため、stdinとして/ dev / nullを使用することをお勧めします。

#!/bin/bash
while read ONELINE ; do
   ssh ubuntu@host_xyz </dev/null <<EOF 2>&1 | filter_pgm 
   echo "Hi, $ONELINE. You come here often?"
   process_response_pgm 
EOF
   if [ ${PIPESTATUS[0]} -ne 0 ] ; then
      echo "aborting loop"
      exit ${PIPESTATUS[0]}
   fi
done << input_list.txt

これは意味がありません。<<EOF上書きされます</dev/nullリダイレクトを。<<後のリダイレクトdoneは間違っています。
tripleee

1

これが起こっていset -eたのgrepは、ループ内に出力がなく(ゼロ以外のエラーコードが返された)ループが返されたためです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.