【问题标题】:How to interact with ssh using subprocess module如何使用子进程模块与 ssh 交互
【发布时间】:2014-01-29 14:15:40
【问题描述】:

我正在尝试使用子进程生成一个 ssh 子进程。

我正在 Windows 7 上开发 Python 2.7.6

这是我的代码:

from subprocess import *
r=Popen("ssh sshserver@localhost", stdout=PIPE)
stdout, stderr=r.communicate()
print(stdout)
print(stderr)

输出:

None

stdout 应包含: sshserver@localhost 的密码:

【问题讨论】:

  • 至少在 Linux(和可能的窗口)上,您的 tty 的 stdin 与 ssh 要求输入密码的不同。等一下,得到了这个的代码(linux tho)。
  • 如果您需要自动化ssh,您可能需要调查pexpect 或类似的东西。您可能还想考虑只使用基于密钥的身份验证,这样您就不需要搞砸密码了。
  • 我已经在 pexpect 上工作过,但它不能在 Windows 上完美运行,这就是为什么我想使用标准 python 模块@larsks
  • @user3249194 您不能为此使用标准模块。 Windows 不允许您连接到子进程 ID。我读过有人在摆弄win32 库,但他设法更改了主循环的进程权限,以便附加到子进程 ID 并读取标准输出。但简而言之,这是不可能的。我已经在下面发布了所有已知有效的 3 个选项。第一个是 Linux 解决方案,所以你可能对此不感兴趣,除非你想掌握生活并使用合适的操作系统,呵呵 :)
  • @Torxed 我已经安装了 debian 并使用了 pexpect,但我想为 windows 找到一个解决方案:D

标签: python subprocess popen


【解决方案1】:

这是一个工作 SSH 代码的示例,它处理证书部分是/否的提示,以及在被要求输入密码时。

#!/usr/bin/python

import pty, sys
from subprocess import Popen, PIPE, STDOUT
from time import sleep
from os import fork, waitpid, execv, read, write

class ssh():
    def __init__(self, host, execute='echo "done" > /root/testing.txt', askpass=False, user='root', password=b'SuperSecurePassword'):
        self.exec = execute
        self.host = host
        self.user = user
        self.password = password
        self.askpass = askpass
        self.run()

    def run(self):
        command = [
                '/usr/bin/ssh',
                self.user+'@'+self.host,
                '-o', 'NumberOfPasswordPrompts=1',
                self.exec,
        ]

        # PID = 0 for child, and the PID of the child for the parent    
        pid, child_fd = pty.fork()

        if not pid: # Child process
            # Replace child process with our SSH process
            execv(command[0], command)

        ## if we havn't setup pub-key authentication
        ## we can loop for a password promt and "insert" the password.
        while self.askpass:
            try:
                output = read(child_fd, 1024).strip()
            except:
                break
            lower = output.lower()
            # Write the password
            if b'password:' in lower:
                write(child_fd, self.password + b'\n')
                break
            elif b'are you sure you want to continue connecting' in lower:
                # Adding key to known_hosts
                write(child_fd, b'yes\n')
            elif b'company privacy warning' in lower:
                pass # This is an understood message
            else:
                print('Error:',output)

        waitpid(pid, 0)

您无法立即读取stdin 的原因(如果我在这里错了,请纠正我)是因为 SSH 在您需要读取/附加到的不同进程 ID 下作为子进程运行。

由于您使用的是 Windows,pty 将无法正常工作。有两种解决方案会更好,那就是pexpect,正如有人指出的基于密钥的身份验证。

为了实现基于密钥的身份验证,您只需执行以下操作: 在您的客户端上,运行:ssh-keygen 将您的id_rsa.pub 内容(一行)复制到服务器上的/home/user/.ssh/authorized_keys

你就完成了。 如果没有,请使用 pexpect。

import pexpect
child = pexpect.spawn('ssh user@host.com')
child.expect('Password:')
child.sendline('SuperSecretPassword')

【讨论】:

  • pty 模块需要 termios 只能在 Unix 平台上运行
  • 我在回答中没有提到哪个? ://
  • 是的,你做到了,我确实提到我正在使用 Windows ;)
  • 这就是为什么我发布了另外两个适用于 Windows 的解决方案;)注意:Linux 修复适用于阅读此内容的任何其他人(因为这是一个常见问题,因此会有很多其他人来到这里),你现在也有一台 linux 机器,所以这对你也有帮助:P
  • 您的答案中的解决方案在 Windows 上不起作用。 pexpect 需要 pty,它需要一个 POSIX 系统(pty 是为 Linux 编写的)。
猜你喜欢
  • 2010-10-01
  • 1970-01-01
  • 2016-12-15
  • 1970-01-01
  • 2014-07-03
  • 1970-01-01
  • 1970-01-01
  • 2018-11-24
  • 1970-01-01
相关资源
最近更新 更多