Paramiko channel stucks when reading large ouput
我在远程Linux机器上执行命令并使用Paramiko读取输出的代码。 代码def看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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() |
上面的代码适用于小输出,但是卡在大输出上。
这里有任何与缓冲区相关的问题吗?
我要发布与Bruce Wayne(:)的输入一起使用的最终代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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 |
我没有看到与stdout通道相关的问题,但是我不确定您处理stderr的方式。您能确认,不是stderr捕获那引起问题的吗?
我会尝试您的代码,并告诉您。
更新:
当您执行的命令在STDERR中给出大量消息时,代码将冻结。我不确定为什么,但是
因此,捕获错误流的方式与捕获标准输出的方式相同。
就像是,
1 2 3 4 5 6 | contents_err = StringIO.StringIO() data_err = chan.recv_stderr(1024) while data_err: contents_err.write(data_err) data_err = chan.recv_stderr(1024) |
您甚至可以先尝试将
实际上,我认为以上所有答案都无法解决真正的问题:
如果远程程序首先产生大量stderr输出,则
1 2 | stdout.readlines() stderr.readlines() |
将永远挂。虽然
1 2 | stderr.readlines() stdout.readlines() |
可以解决这种情况,但是如果远程程序首先产生大量stdout输出,它将失败。
我还没有解决方案...
如果使用打开的ssh会话的高级表示,则会更容易。由于您已经使用ssh-client打开频道,因此您可以从那里运行命令,而无需执行其他工作。
1 2 3 4 5 6 7 8 9 | 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 |
如果您随后收到其他数据,则需要再次从这些文件句柄中读取。
TL; DR:如果使用
如果使用@Spencer Rathbun的答案:
1 2 3 4 5 | 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) |
您可能想知道大输出可能带来的限制。
实验上,
Spencer使用的一种简单方法就是-在尝试读取
我之所以发布这个帖子,是因为我很笨,花了很长时间,试图弄清楚我是如何破坏代码的,答案是我从
为了使paramiko命令表现得像subprocess.call一样,您可以使用以下代码(经过python-3.5和paramiko-2.1.1测试):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #!/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') |