提问人:bcbishop 提问时间:12/10/2012 最后编辑:oguz ismailbcbishop 更新时间:12/28/2022 访问量:91895
While 循环在 Bash 的第一行之后停止读取
While loop stops reading after the first line in 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
答:
问题是运行命令,默认情况下从 stdin(您的输入文件)读取。因此,您只能看到已处理的第一行,因为该命令会占用文件的其余部分,并且 while 循环会终止。do_work.sh
ssh
ssh
这不仅发生在 上,还发生在任何读取 stdin 的命令上,包括 、 、 、 、 等。ssh
mplayer
ffmpeg
HandBrakeCLI
httpie
brew install
为防止出现这种情况,请将该选项传递给命令,使其读取自 而不是 stdin。其他命令具有类似的标志,或者您可以普遍使用 .-n
ssh
/dev/null
< /dev/null
评论
猫
。你会认为啮齿动物会特别警惕这一点。
while read host ; do $host do_something ; done < /etc/hosts
会避免它。这真是救命稻草,谢谢!
httpie
是另一个默认读取 STDIN 的命令,在 bash 或 fish 循环中调用时将遭受相同的行为。使用或设置标准输入如上所述。http --ignore-stdin
/dev/null
gcloud compute ssh
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
评论
<<EOF
</dev/null
<<
done
这发生在我身上,因为我有一个循环返回,没有输出(这给出了一个非零错误代码)。set -e
grep
评论
更一般地说,一种不特定于的解决方法是重定向任何命令的标准输入,否则这些命令可能会消耗循环的输入。ssh
while
while read -r line; do
((count++))
echo "$count $line"
sh ./do_work.sh "$line" </dev/null
done < "$filename"
添加 是这里的关键点,尽管更正后的引用对于稳健性也很重要;另请参阅何时将引号括在 shell 变量周围?。除非您特别要求在输入中为不带 .最后,避免私有变量使用大写字母。</dev/null
read -r
-r
另一种特定的解决方法是确保任何命令都绑定了其标准输入,例如通过更改ssh
ssh
ssh otherhost some commands here
而是从 here 文档中读取命令,该文档方便地(对于此特定方案)绑定了 for the commands 的标准输入:ssh
ssh otherhost <<'____HERE'
some commands here
____HERE
一个非常简单和可靠的解决方法是更改命令从中接收输入的文件描述符。read
这是通过两个修改来实现的:对 的参数和 的重定向运算符。-u
read
< $FILENAME
在 BASH 中,默认文件描述符值(即 in 的值)为:-u
read
- 0 = 标准
- 1 = 标准输出
- 2 = stderr
因此,只需选择一些其他未使用的文件描述符,例如只是为了好玩。9
因此,以下是解决方法:
while read -u 9 LINE; do
let count++
echo "$count $LINE"
sh ./do_work.sh $LINE
done 9< $FILENAME
请注意以下两个修改:
read
成为read -u 9
< $FILENAME
成为9< $FILENAME
作为最佳实践,我对我在 BASH 中编写的所有循环都这样做。
如果您有嵌套循环 using ,则对每个循环使用不同的文件描述符 (9,8,7,...)。while
read
评论
sloccount
评论
source
exec
-e
do_work.sh
ssh
do_work.sh
do.sh
set -x