从 bash 脚本中通过 ssh 在远程主机上执行命令

Posted

技术标签:

【中文标题】从 bash 脚本中通过 ssh 在远程主机上执行命令【英文标题】:Execute a command on remote hosts via ssh from inside a bash script 【发布时间】:2012-01-12 15:40:54 【问题描述】:

我编写了一个 bash 脚本,它应该从文件中读取用户名和 IP 地址并通过 ssh 对它们执行命令。

这是 hosts.txt :

user1 192.168.56.232
user2 192.168.56.233

这是 myScript.sh :

cmd="ls -l"

while read line
do
   set $line
   echo "HOST:" $1@$2
   ssh $1@$2 $cmd
   exitStatus=$?
   echo "Exit Status: " $exitStatus
done < hosts.txt

问题是在第一个主机完成后执行似乎停止了。这是输出:

$ ./myScript.sh
HOST: user1@192.168.56.232
total 2748
drwxr-xr-x 2 user1 user1    4096 2011-11-15 20:01 Desktop
drwxr-xr-x 2 user1 user1    4096 2011-11-10 20:37 Documents
...
drwxr-xr-x 2 user1 user1    4096 2011-11-10 20:37 Videos
Exit Status:  0
$

为什么会有这样的行为,我该如何解决?

【问题讨论】:

【参考方案1】:

在您的脚本中,ssh 作业与read line 获得相同的标准输入,并且在您的情况下,恰好在第一次调用时吃光了所有行。所以read line 只能看到 输入的第一行。

解决方案:关闭ssh 的标准输入,或者更好地从/dev/null 重定向。 (一些程序 不喜欢关闭标准输入)

while read line
do
    ssh server somecommand </dev/null    # Redirect stdin from /dev/null
                                         # for ssh command
                                         # (Does not affect the other commands)
    printf '%s\n' "$line"
done < hosts.txt

如果您不想为循环内的每个作业从 /dev/null 重定向,您也可以尝试以下方法之一:

while read line
do
  
    commands...
   </dev/null                           # Redirect stdin from /dev/null for all
                                         # commands inside the braces
done < hosts.txt


# In the following, let's not override the original stdin. Open hosts.txt on fd3
# instead

while read line <&3   # execute read command with fd0 (stdin) backed up from fd3
do
    commands...       # inside, you still have the original stdin
                      # (maybe the terminal) from outside, which can be practical.

done 3< hosts.txt     # make hosts.txt available as fd3 for all commands in the
                      # loop (so fd0 (stdin) will be unaffected)


# totally safe way: close fd3 for all inner commands at once

while read line <&3
do
  
    commands...
   3<&-
done 3< hosts.txt

【讨论】:

【参考方案2】:

您遇到的问题是 SSH 进程正在消耗所有标准输入,因此在第一个 ssh 命令运行后 read 看不到任何输入。您可以使用 SSH 的 -n 标志来防止这种情况发生,或者您可以将 /dev/null 重定向到 ssh 命令的标准输入。

有关详细信息,请参阅以下内容: http://mywiki.wooledge.org/BashFAQ/089

【讨论】:

【参考方案3】:

确保ssh 命令不会使用ssh -n 从hosts.txt 中读取

【讨论】:

它没有。它只使用 while 循环中的变量:ssh $1@$2 $cmd 然后试试echo|ssh $1@$2 $cmd @kavakli ...看看会发生什么【参考方案4】:

我觉得你的问题不必要地冗长..

基本上你应该能够重现问题:

while read line
do
   echo $line
done < hosts.txt

哪个应该可以正常工作..您编辑了正确的文件吗?里面有特殊字符吗?使用适当的编辑器(例如:vim)检查它。

【讨论】:

他可能无法重现该问题,因为 echo 不读取标准输入。

以上是关于从 bash 脚本中通过 ssh 在远程主机上执行命令的主要内容,如果未能解决你的问题,请参考以下文章

通过 ssh 在远程主机上启动 bash 脚本

本机远程主机的脚本执行

怎么在一个shell脚本中执行远程主机的另一个shell脚本?? ssh已经可以无密码登录了。

通过 ssh 会话执行 bash 别名 | Linux 中国

利用shell脚本执行ssh远程另一台主机执行命令并返回命令的结果集

bash 脚本中的 ssh“端口 22:没有到主机的路由”错误