而循环在 Bash 中的第一行之后停止读取



>我有以下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命令。

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

这不仅发生在ssh,也发生在任何读取 stdin(包括 mplayerffmpegHandBrakeCLIhttpiebrew install 等)上。

若要防止出现这种情况,请将 -n 选项传递给 ssh 命令,使其从/dev/null而不是 stdin 读取。其他命令具有类似的标志,或者您可以普遍使用 < /dev/null

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

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

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

  • 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,...)。

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

while read -r line; do
   ((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

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

ssh otherhost <<'____HERE'
    some commands here
____HERE

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

这发生在我身上,因为我有set -e并且循环中的grep返回没有输出(给出非零错误代码)。

相关内容

  • 没有找到相关文章

最新更新