web-dev-qa-db-ja.com

読み取り行ループ中のシェルスクリプトは、最初の行の後に停止します

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

#!/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コマンドを実行します。

83
bcbishop

問題は、do_work.shsshコマンドを実行し、デフォルトでsshが入力ファイルであるstdinから読み取ることです。その結果、sshがファイルの残りを消費し、whileループが終了するため、処理された最初の行のみが表示されます。

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

149
dogbane

ssh -nオプションは、HEREdocを使用して出力を別のプログラムにパイプするときに、sshの終了ステータスをチェックしません。そのため、/ 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
5
jacobm654321

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

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

ここで</dev/nullを追加することが重要です(ただし、修正された引用もいくらか重要です。 シェル変数を引用符で囲む場合 も参照してください)。 read -rなしで得られる従来のわずかに奇妙な動作を特に必要としない限り、-rを使用する必要があります。

sshにある程度固有のソートの別の回避策は、sshコマンドの標準入力が固定されていることを確認することです。変更することにより

ssh otherhost some commands here

代わりにヒアドキュメントからコマンドを読むと、(この特定のシナリオでは)コマンドのsshの標準入力が便利に結び付けられます:

ssh otherhost <<'____HERE'
    some commands here
____HERE
2
tripleee