Python Paramiko SSH

23

我是Python的新手。我编写了一个脚本来连接到主机并执行一条命令。

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, username=user, password=pw)

print 'running remote command'

stdin, stdout, stderr = ssh.exec_command(command)
stdin.close()

for line in stdout.read().splitlines():
    print '%s$: %s' % (host, line)
    if outfile != None:
        f_outfile.write("%s\n" %line)

for line in stderr.read().splitlines():
    print '%s$: %s' % (host, line + "\n")
    if outfile != None:
        f_outfile.write("%s\n" %line)

ssh.close()

if outfile != None:
    f_outfile.close()

print 'connection to %s closed' %host

except:
   e = sys.exc_info()[1]
   print '%s' %e

当远程命令不需要tty时,这段代码可以正常工作。我在Paramiko中嵌套SSH会话找到了一个invoke_shell示例。但是我对这个解决方案不太满意,因为如果服务器上有一个未在我的脚本中指定的提示符,则会导致无限循环;或者,如果脚本中指定的提示符是返回文本中的字符串,则可能收不到所有数据。也许有更好的解决方案,可以像我的脚本一样将标准输出和标准错误发送回来吗?


请注意,此答案未使用SSH密钥,因此需要密码。有了SSH密钥,您无需密码,而是传递key_filename参数即可。 - questionto42
6个回答

36

被接受的答案有问题,有时会随机地从服务器获取裁剪过的响应。我不知道为什么,也没有调查接受的答案错误的原因,因为这段代码对我来说完美运行:

import paramiko

ip='server ip'
port=22
username='username'
password='password'

cmd='some useful command' 

ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip,port,username,password)

stdin,stdout,stderr=ssh.exec_command(cmd)
outlines=stdout.readlines()
resp=''.join(outlines)
print(resp)

stdin,stdout,stderr=ssh.exec_command('some really useful command')
outlines=stdout.readlines()
resp=''.join(outlines)
print(resp)

1
我也见过这种情况,就像是 exit_status_ready() 和 recv_ready() 之间的竞争条件。 - robert
2
如果有人想使用上述方法捕获退出代码,请注意 - stdout.channel.recv_exit_status() - Koshur
有一个异常:密钥无法用于签名。 - user3072843

27

你可以在http://docs.paramiko.org/en/stable/index.html找到详细的paramiko API文档。

我使用以下方法在受密码保护的客户端上执行命令:

import paramiko

nbytes = 4096
hostname = 'hostname'
port = 22
username = 'username' 
password = 'password'
command = 'ls'

client = paramiko.Transport((hostname, port))
client.connect(username=username, password=password)

stdout_data = []
stderr_data = []
session = client.open_channel(kind='session')
session.exec_command(command)
while True:
    if session.recv_ready():
        stdout_data.append(session.recv(nbytes))
    if session.recv_stderr_ready():
        stderr_data.append(session.recv_stderr(nbytes))
    if session.exit_status_ready():
        break

print 'exit status: ', session.recv_exit_status()
print ''.join(stdout_data)
print ''.join(stderr_data)

session.close()
client.close()

1
应该使用session.recv(4096)session.recv_stderr(4096),而不是data_block。例如(data_block从哪里来?)。 - Doncho Gunchev
你说得对,data_block就是你描述的那样(recv/recv_stderr),但我不小心删掉了那些行。 - ThePracticalOne
截至2015年5月,Paramiko文档在此处:http://docs.paramiko.org/en/1.15/index.html - Jeremiah
1
截至2015年12月,Paramiko文档(版本1.16)在此处:http://docs.paramiko.org/en/1.16/index.html - Amir Katz
这段代码有问题:它能够工作,但有时候响应不完整。 - Raiden Core
这段代码是否执行相同的操作?https://dev59.com/9F4c5IYBdhLWcg3wCGRJ#28411997 - TheCrazyProfessor

0

@ThePracticalOne的代码非常适合展示用法,除了一件事情: 有时候输出会不完整。(session.recv_ready()if session.recv_ready():之后变为true,而session.recv_stderr_ready()session.exit_status_ready()在进入下一个循环之前就已经变为true)

所以我的想法是在会话退出时检索数据。

while True:
if session.exit_status_ready():
while True:
    while True:
        print "try to recv stdout..."
        ret = session.recv(nbytes)
        if len(ret) == 0:
            break
        stdout_data.append(ret)

    while True:
        print "try to recv stderr..."
        ret = session.recv_stderr(nbytes)
        if len(ret) == 0:
            break
        stderr_data.append(ret)
    break

0
ThePracticalOne - 你真是个英雄! 我在使用Client中的exec_command时遇到了问题。 我尝试在Windows服务器上通过ssh运行powershell命令,只有你的示例才能正常工作。
client = paramiko.Transport((hostname, port))
client.connect(username=username, password=password)

并且

 while True:
        if session.recv_ready():
            stdout_data.append(session.recv(nbytes))
        if session.recv_stderr_ready():
            stderr_data.append(session.recv_stderr(nbytes))
        if session.exit_status_ready():
            break

对我很有帮助!


0

无密码SSH对我有用

import paramiko

def connect_SSH():
    ssh = paramiko.SSHClient()
    username = '<uname>'
    port = <port-no>
    ip = '<ip-address>'
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(ip,port,username)

    stdin, stdout, stderr = ssh.exec_command('<cmd>')
    outlines = stdout.readlines()
    resp=''.join(outlines)
    print(resp) 
connect_SSH()

-1
###### Use paramiko to connect to LINUX platform############
import paramiko

ip='server ip'
port=22
username='username'
password='password'
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip,port,username,password)

--------Connection Established-----------------------------

######To run shell commands on remote connection###########
import paramiko

ip='server ip'
port=22
username='username'
password='password'
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip,port,username,password)


stdin,stdout,stderr=ssh.exec_command(cmd)
outlines=stdout.readlines()
resp=''.join(outlines)
print(resp) # Output 

1
请参考:https://github.com/sarthakjain1811/Python-Scripts/blob/master/Connect%20to%20remote%20linux%20connection%20and%20run%20shell%20commands - Sarthak Jain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接