我有一个代码,我在远程Linux机器上执行命令并使用Paramiko读取输出。代码def看起来像这样:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, username=user['username'], password=user['password'])
chan = self.ssh.get_transport().open_session()
chan.settimeout(10800)
try:
# Execute thecommand
chan.exec_command(cmd)
contents = StringIO.StringIO()
data = chan.recv(1024)
# Capturing data from chan buffer.
while data:
contents.write(data)
data = chan.recv(1024)
except socket.timeout:
raise socket.timeout
output = contents.getvalue()
return output,chan.recv_stderr(600),chan.recv_exit_status()
上面的代码适用于小输出,但它会因较大的输出而卡住。
这里有缓冲相关的问题吗?
答案 0 :(得分:7)
我发布了最终代码,该代码使用了Bruce Wayne(:))
的输入ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, username=user['username'], password=user['password'])
chan = self.ssh.get_transport().open_session()
chan.settimeout(10800)
try:
# Execute the given command
chan.exec_command(cmd)
# To capture Data. Need to read the entire buffer to capture output
contents = StringIO.StringIO()
error = StringIO.StringIO()
while not chan.exit_status_ready():
if chan.recv_ready():
data = chan.recv(1024)
#print "Indside stdout"
while data:
contents.write(data)
data = chan.recv(1024)
if chan.recv_stderr_ready():
error_buff = chan.recv_stderr(1024)
while error_buff:
error.write(error_buff)
error_buff = chan.recv_stderr(1024)
exit_status = chan.recv_exit_status()
except socket.timeout:
raise socket.timeout
output = contents.getvalue()
error_value = error.getvalue()
return output, error_value, exit_status
答案 1 :(得分:3)
我发现没有与stdout频道有关的问题,但我不确定你处理stderr的方式。你能否证实,它不是捕获导致问题的stderr? 我会尝试你的代码并告诉你。
更新:
当您执行的命令在STDERR中提供大量消息时,您的代码会冻结。我不确定为什么,但recv_stderr(600)
可能是原因。
因此捕获错误流的方式与捕获标准输出的方式相同。
像,
contents_err = StringIO.StringIO()
data_err = chan.recv_stderr(1024)
while data_err:
contents_err.write(data_err)
data_err = chan.recv_stderr(1024)
您甚至可以先尝试将recv_stderr(600)
更改为recv_stderr(1024)
或更高。
答案 2 :(得分:2)
实际上我认为以上所有答案都无法解决真正的问题:
如果远程程序首先产生大量的 stderr输出,那么
stdout.readlines()
stderr.readlines()
会永远挂起来。虽然
stderr.readlines()
stdout.readlines()
会解决这种情况,但如果远程程序首先产生大量的标准输出,它将会失败。
我还没有解决方案......
答案 3 :(得分:0)
如果使用开放ssh会话的高级表示,则会更容易。由于您已经使用ssh-client打开频道,因此您可以从那里运行命令,避免额外的工作。
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, username=user['username'], password=user['password'])
stdin, stdout, stderr = ssh.exec_command(cmd)
for line in stdout.readlines():
print line
for line in stderr.readlines():
print line
如果您之后收到其他数据,则需要再次从这些文件句柄中读取。
答案 4 :(得分:0)
要使paramiko命令的行为类似于subprocess.call,您可以使用这段代码(使用python-3.5和paramiko-2.1.1测试):
#!/usr/bin/env /usr/bin/python3
import os
import sys
from paramiko import SSHClient, AutoAddPolicy
from socket import getfqdn
class SecureSHell(object):
reuser = os.environ['USER']
remote = ''
def __init__(self, *args, **kwargs):
for arg in args:
if hasattr(self, arg):
setattr(self, arg, True)
for (key, val) in kwargs.items():
if hasattr(self, key):
setattr(self, key, val)
@staticmethod
def _ssh_(remote, reuser, port=22):
if '@' in remote:
_reuser, remote = remote.split('@')
_fqdn = getfqdn(remote)
remote = _fqdn if _fqdn else remote
ssh = SSHClient()
ssh.set_missing_host_key_policy(AutoAddPolicy())
ssh.connect(remote, int(port), username=reuser)
return ssh
def call(self, cmd, remote=None, reuser=None):
remote = remote if remote else self.remote
reuser = reuser if reuser else self.reuser
ssh = self._ssh_(remote, reuser)
chn = ssh.get_transport().open_session()
chn.settimeout(10800)
chn.exec_command(cmd)
while not chn.exit_status_ready():
if chn.recv_ready():
och = chn.recv(1024)
while och:
sys.stdout.write(och.decode())
och = chn.recv(1024)
if chn.recv_stderr_ready():
ech = chn.recv_stderr(1024)
while ech:
sys.stderr.write(och.decode())
ech = chn.recv_stderr(1024)
return int(chn.recv_exit_status())
ssh = SecureSHell(remote='example.com', user='d0n')
ssh.call('find')
答案 5 :(得分:0)
TL; DR:如果使用stdout.readlines()
,则在stderr.readlines()
之前致电ssh.exec_command()
如果您使用@Spencer Rathbun的答案:
sh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, username=user['username'], password=user['password'])
stdin, stdout, stderr = ssh.exec_command(cmd)
您可能想知道大输出可能带来的限制。
根据实验,stdin, stdout, stderr = ssh.exec_command(cmd)
将无法立即将完整输出写入stdout
和stderr
。更具体地说,在填充之前,缓冲区似乎包含2^21
(2,097,152)个字符。如果任何缓冲区已满,则exec_command
将在写入该缓冲区时阻塞,并保持阻塞状态,直到该缓冲区被清空为止。这意味着如果您的stdout
太大,您将无法读取stderr
,因为在任何一个缓冲区中,直到它可以写入完整的输出,您都不会收到EOF。
解决此问题的简单方法是Spencer使用的方法-在尝试读取stdout.readlines()
之前,先通过stderr
获取所有正常输出。仅当您在2^21
中有超过stderr
个字符时,此操作才会失败,这在我的用例中是可以接受的限制。
我之所以发布此帖子,是因为我很笨,花了很长时间,试图弄清楚我的代码是如何破解的,答案是我从stderr
之前stdout
开始阅读},而我的stdout
太大,无法容纳在缓冲区中。