我有一个文件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 输入必须切换到另一个源。