【问题标题】:Execute a command on remote hosts via ssh from inside a bash script从 bash 脚本中通过 ssh 在远程主机上执行命令
【发布时间】:2025-12-19 07:10:16
【问题描述】:

我编写了一个 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
$

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

【问题讨论】:

    标签: bash shell ssh openssh


    【解决方案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 不读取标准输入。