使用标准python库输入ssh密码(不是pexpect)

时间:2014-07-11 00:53:29

标签: python ssh subprocess

相关问题本质上是在问同样的问题,但答案对我不起作用:

Make python enter password when running a csh script

How to interact with ssh using subprocess module

How to execute a process remotely using python

我想ssh到远程计算机并运行一个命令。例如:

ssh <user>@<ipv6-link-local-addr>%eth0 sudo service fooService status

问题是我试图通过只有标准库(没有pexpect)的python脚本来实现这一点。我一直试图使用subprocess模块使其工作,但是在请求密码时调用communicate总是阻塞,即使我提供了密码作为communicate的参数。例如:

proc = subprocess.Popen(
        [
            "ssh",
            "{testUser1}@{testHost1}%eth0".format(**locals()),
            "sudo service cassandra status"],
        shell=False,
        stdin=subprocess.PIPE)
a, b = proc.communicate(input=testPasswd1)
print "a:", a, "b:", b
print "return code: ", proc.returncode

我也尝试了上述的一些变体(例如,删除“input =”,添加/删除subprocess.PIPE stdoutsterr的{​​{1}}分配。但是,结果始终是相同的提示:

ubuntu@<ipv6-link-local-addr>%eth0's password:

我错过了什么吗?或者有另一种方法可以使用python标准库实现这一目标吗?

1 个答案:

答案 0 :(得分:5)

这个答案只是Torxed对this answer的改编,我建议你去投票。它只是添加了捕获您在远程服务器上执行的命令输出的功能。

import pty
from os import 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')
            else:
                print('Error:',output)

        # See if there's more output to read after the password has been sent,
        # And capture it in a list.
        output = []
        while True:
            try:
                output.append(read(child_fd, 1024).strip())
            except:
                break

        waitpid(pid, 0)
        return ''.join(output)

if __name__ == "__main__":
    s = ssh("some ip", execute="ls -R /etc", askpass=True)
    print s.run()

输出:

/etc:
adduser.conf
adjtime
aliases
alternatives
apm
apt
bash.bashrc
bash_completion.d
<and so on>