读取行循环后的Shell脚本在第一行后停止

时间:2012-12-10 11:38:41

标签: bash shell ssh while-loop

我有以下shell脚本。目的是循环到目标文件的每一行(其路径是脚本的输入参数)并对每一行进行操作。现在,它似乎只适用于目标文件中的第一行,并在该行被处理后停止。我的剧本有什么问题吗?

#!/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命令。

4 个答案:

答案 0 :(得分:142)

问题是do_work.sh运行ssh命令,默认情况下ssh从作为输入文件的stdin读取。因此,您只看到处理的第一行,因为ssh使用了文件的其余部分,而您的while循环终止。

要防止这种情况,请将-n选项传递给ssh命令,使其从/dev/null而不是标准输入读取。

答案 1 :(得分:7)

一个非常简单且可靠的解决方法是更改​​ read 命令从中接收输入的文件描述符

这是通过两个修改完成的:-uread 参数和 < $FILENAME 的重定向运算符。

在 BASH 中,默认文件描述符值(即 -uread 的值)是:

  • 0 = 标准输入
  • 1 = 标准输出
  • 2 = 标准错误

所以只需选择一些其他未使用的文件描述符,例如 9 只是为了好玩。

因此,以下是解决方法:

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

注意两个修改:

  1. read 变成 read -u 9
  2. < $FILENAME 变成 9< $FILENAME

作为最佳实践,我对我用 BASH 编写的所有 while 循环都这样做。 如果您有使用 read 的嵌套循环,请为每个循环使用不同的文件描述符(9,8,7,...)。

答案 2 :(得分:5)

ssh -n选项可防止在将输出传递给另一个程序时使用HEREdoc时检查ssh的退出状态。 因此,首选使用/ dev / null作为stdin。

#!/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

答案 3 :(得分:1)

更一般地说,不是ssh特有的解决方法是将标准输入重定向到任何可能会消耗while循环输入的命令。

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

这里添加</dev/null是关键点(尽管正确的报价也很重要;另请参见When to wrap quotes around a shell variable?)。

另一种特定于ssh的变通方法是确保任何ssh命令都捆绑了其标准输入,例如通过更改

ssh otherhost some commands here

而是从here文档中读取命令,该文档很方便地(对于这种特定情况)将ssh的标准输入与命令绑定在一起:

ssh otherhost <<'____HERE'
    some commands here
____HERE