解决方案

解决方案

我有一个文件servers.txt,其中包含服务器列表:

server1.mydomain.com
server2.mydomain.com
server3.mydomain.com

当我逐行读取文件while并回显每一行时,一切都按预期进行。所有行均被打印。

$ while read HOST ; do echo $HOST ; done < servers.txt
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com

但是,当我想要 ssh 到所有服务器并执行命令时,我的while循环突然停止工作:

$ while read HOST ; do ssh $HOST "uname -a" ; done < servers.txt
Linux server1 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux

这仅连接到列表中的第一个服务器,而不是所有服务器。我不明白这里发生了什么。有人可以解释一下吗?

这更奇怪,因为使用for循环效果很好:

$ for HOST in $(cat servers.txt ) ; do ssh $HOST "uname -a" ; done
Linux server1 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Linux server2 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Linux server3 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux

它必须是特定于 的命令ssh,因为其他命令可以正常工作,例如ping

$ while read HOST ; do ping -c 1 $HOST ; done < servers.txt

答案1

ssh正在读取标准输入的其余部分。

while read HOST ; do … ; done < servers.txt

read从标准输入读取。<从文件重定向标准输入。

不幸的是,您尝试运行的命令也会读取标准输入,因此它最终会吃掉文件的其余部分。您可以通过以下方式清楚地看到它:

$ while read HOST ; do echo start $HOST end; cat; done < servers.txt 
start server1.mydomain.com end
server2.mydomain.com
server3.mydomain.com

注意如何cat删除(并回显)剩下的两行。 (已按预期完成,每行都会在主机周围有“开始”和“结束”。)

为什么for有效?

您的for线路不会重定向到标准输入。 (事实上​​,它servers.txt在第一次迭代之前将文件的全部内容读入内存)。因此,ssh继续从终端读取其标准输入(或者可能什么也不读取,具体取决于脚本的调用方式)。

解决方案

至少在 bash 中,您可以read使用不同的文件描述符。

while read -u10 HOST ; do ssh $HOST "uname -a" ; done 10< servers.txt
#          ^^^^                                       ^^

应该可以工作。10只是我选择的任意文件号。 0、1 和 2 具有已定义的含义,通常打开文件将从第一个可用数字开始(因此接下来使用 3)。因此,10 足够高,不会妨碍,但又足够低,低于某些 shell 的限制。再加上它是一个不错的整数......

替代解决方案 1:-n

作为麦克尼斯指出在他/她的回答,OpenSSH 客户端有一个-n选项可以阻止它读取标准输入。这在 的特定情况下效果很好ssh,但当然其他命令可能缺乏这一点 - 无论哪个命令正在占用您的标准输入,其他解决方案都可以工作。

替代解决方案 2:第二次重定向

显然,你可以(例如,我尝试过,它至少在我的 Bash 版本中有效......)进行第二次重定向,如下所示:

while read HOST ; do ssh $HOST "uname -a" < /dev/null; done < servers.txt

您可以将其与任何命令一起使用,但如果您确实希望终端输入进入该命令,则会很困难。

答案2

正如德罗伯特所描述的,ssh阅读你的stdin.

要更改此行为,您可以添加 -n no ssh 以防止它读取标准输入。

ssh -n $HOST "uname -a"

答案3

你实际上可能最好使用噗噗来自并行 ssh项目。

pssh -h $hostfile -t $timeout -i $commands

-i意思是互动。 pssh 还附带并行 scp 和并行 rsync。好处是它异步运行,并且可以根据您的要求运行尽可能多的线程。默认值(非 -i/interactive)是输出到 stdout/stderr 的单独目录,这是通过 $outputdir/$hostname 执行的。

答案4

因为 ssh 命令从标准输入获取所有流,由 while 语句提供,

您可以使用管道将 ssh 的标准输入切换到另一个源:

echo "" | ssh ...

例子 :

while read HOST ; do echo "" | ssh $HOST "uname -a" ; done < servers.txt

while 循环中所有 ssh 命令的 stdin 输入必须切换到另一个源。

相关内容