我有以下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
命令。
答案 0 :(得分:142)
问题是do_work.sh
运行ssh
命令,默认情况下ssh
从作为输入文件的stdin读取。因此,您只看到处理的第一行,因为ssh
使用了文件的其余部分,而您的while循环终止。
要防止这种情况,请将-n
选项传递给ssh
命令,使其从/dev/null
而不是标准输入读取。
答案 1 :(得分:7)
一个非常简单且可靠的解决方法是更改 read
命令从中接收输入的文件描述符。
这是通过两个修改完成的:-u
的 read
参数和 < $FILENAME
的重定向运算符。
在 BASH 中,默认文件描述符值(即 -u
中 read
的值)是:
所以只需选择一些其他未使用的文件描述符,例如 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 编写的所有 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