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

bcb*_*hop 86 ssh bash shell 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"
Run Code Online (Sandbox Code Playgroud)

do_work.sh,我运行了几个ssh命令.

dog*_*ane 154

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

要防止这种情况,请将-n选项传递给您的ssh命令,使其从/dev/nullstdin而不是stdin 读取.

  • 或者可能是`sh ./do_work.sh $ LINE </ dev/null` (28认同)
  • @rat你仍然想避免[无用的`猫`。](/questions/11710552/useless-use-of-cat)你认为啮齿类动物尤其会对此保持警惕。 (4认同)
  • 非常有用,帮助我运行这个 zsh oneliner:cat Hosts | 读取主机时;执行 ssh $host do_something ;完毕 (2认同)

tri*_*eee 21

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

while read -r LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh "$LINE" </dev/null
done < "$FILENAME"
Run Code Online (Sandbox Code Playgroud)

的添加</dev/null是这里的关键点(尽管更正的引用也有些重要;另请参阅何时将引号括在 shell 变量周围?)。您将要使用的read -r,除非你特别要求传统有点奇怪的行为,你没有-r

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

ssh otherhost some commands here
Run Code Online (Sandbox Code Playgroud)

改为从 here 文档中读取命令,该文档方便地(对于此特定场景)绑定了ssh命令的标准输入:

ssh otherhost <<'____HERE'
    some commands here
____HERE
Run Code Online (Sandbox Code Playgroud)


cmo*_*cmo 10

一个非常简单的和鲁棒的解决方法是改变文件描述符从该read命令接收输入。

这是通过两个修改来实现的:-u参数 toread和重定向运算符 for < $FILENAME

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

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

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

因此,以下将是解决方法:

while read -u 9 LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh $LINE
done 9< $FILENAME
Run Code Online (Sandbox Code Playgroud)

注意两个修改:

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

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

  • 非常感谢!这看起来对我来说也是最好的解决方案,因为有了这个,如果您有更大的脚本,您就不需要找到有问题的命令。 (3认同)
  • 谢谢!IMO 这实际上是最好的解决方案 (2认同)

jac*_*321 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
Run Code Online (Sandbox Code Playgroud)