Mar*_*ter 95 scripting ssh io-redirection
我有一个文件servers.txt,其中包含服务器列表:
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com
Run Code Online (Sandbox Code Playgroud)
当我逐行读取文件while并回显每一行时,一切都按预期工作。打印所有行。
$ while read HOST ; do echo $HOST ; done < servers.txt
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com
Run Code Online (Sandbox Code Playgroud)
但是,当我想通过 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
Run Code Online (Sandbox Code Playgroud)
这仅连接到列表中的第一台服务器,而不是所有服务器。我不明白这里发生了什么。有人可以解释一下吗?
这更奇怪,因为使用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
Run Code Online (Sandbox Code Playgroud)
它必须特定于ssh,因为其他命令可以正常工作,例如ping:
$ while read HOST ; do ping -c 1 $HOST ; done < servers.txt
Run Code Online (Sandbox Code Playgroud)
der*_*ert 122
ssh 正在阅读标准输入的其余部分。
while read HOST ; do … ; done < servers.txt
Run Code Online (Sandbox Code Playgroud)
read从标准输入读取。该<重定向从文件标准输入。
不幸的是,您尝试运行的命令也会读取 stdin,因此它最终会占用您文件的其余部分。你可以清楚地看到它:
$ while read HOST ; do echo start $HOST end; cat; done < servers.txt
start server1.mydomain.com end
server2.mydomain.com
server3.mydomain.com
Run Code Online (Sandbox Code Playgroud)
注意cat其余两行是如何吃(和回声)的。(按照预期阅读完成,每一行都会在主机周围有“开始”和“结束”。)
为什么for有效?
您的for线路不会重定向到标准输入。(实际上,它servers.txt在第一次迭代之前将文件的全部内容读入内存)。所以ssh继续从终端读取它的标准输入(或者可能什么都不读取,取决于你的脚本是如何调用的)。
至少在 bash 中,您可以read使用不同的文件描述符。
while read -u10 HOST ; do ssh $HOST "uname -a" ; done 10< servers.txt
# ^^^^ ^^
Run Code Online (Sandbox Code Playgroud)
应该工作。10只是我选择的任意文件号。0、1 和 2 具有定义的含义,通常打开文件将从第一个可用数字开始(因此接下来使用 3)。因此,10 足够高以避开障碍,但又足够低以在某些炮弹中低于限制。加上它的一个不错的整数...
正如McNisse在他/她的回答中指出的那样,OpenSSH 客户端有一个-n选项可以阻止它读取标准输入。这在 的特殊情况下效果很好ssh,但当然其他命令可能缺乏这一点 - 无论哪个命令正在吃你的标准输入,其他解决方案都可以工作。
您显然可以(就像我尝试过的那样,它至少在我的 Bash 版本中有效......)进行第二次重定向,看起来像这样:
while read HOST ; do ssh $HOST "uname -a" < /dev/null; done < servers.txt
Run Code Online (Sandbox Code Playgroud)
您可以将它与任何命令一起使用,但如果您真的希望终端输入进入命令,这将很困难。
小智 40
正如 derobert 描述的那样ssh读取您的stdin.
要更改此行为,您可以添加 -n no ssh 以防止它读取标准输入。
ssh -n $HOST "uname -a"
Run Code Online (Sandbox Code Playgroud)
小智 11
实际上,您最好使用来自parallel-ssh项目的pssh。
pssh -h $hostfile -t $timeout -i $commands
Run Code Online (Sandbox Code Playgroud)
-i意味着互动。pssh 还带有并行 scp 和并行 rsync。好处是它异步运行,并且会运行您要求的尽可能多的线程。默认(非 -i/interactive)是输出到 stdout/stderr 的单独目录,它由 $outputdir/$hostname 执行。