我正在使用 paramiko 收集远程主机上的一些信息并在阅读时遇到问题(read()
/readline()
/readlines()
) 来自stderr
渠道。
有时stderr.read()
返回一个空字符串,在我看来,它看起来像是竞争条件的结果。
然而,根据我在互联网上找到的文档和示例,这似乎是正确的方法。
我还尝试开设专用频道并利用chan.recv_ready()
/ chan.recv_stderr_ready()
并通过循环从各个通道读取chan.recv()
/ chan.recv_stderr()
然而,这会导致相同的行为。
这是一个最小的测试用例,在我的设置中,它可靠地导致了这种行为。
import paramiko
class SSH:
def __init__(self):
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect('127.0.0.1', port=31337, username='root', password='root')
self.stdout = ''
self.stderr = ''
self.exit_code = 0
def _run_cmd(self, cmd):
self.stdout = ''
self.stderr = ''
stdin, stdout, stderr = self.ssh.exec_command(cmd)
self.stdout = stdout.read()
self.stderr = stderr.read()
while not stdout.channel.exit_status_ready():
pass
self.exit_code = stdout.channel.recv_exit_status()
if self.exit_code:
print("ERROR: " + self.stderr)
def process_list(self):
self._run_cmd('ls /proc/ | grep -E "^[0-9]+$" | grep -v $$')
lines = self.stdout.split('\n')[:-1]
data = []
for process in lines:
process_data = {}
process_data['pid'] = int(process)
# fetching and parsing process status information from /proc/[PID]/status
self._run_cmd('cat /proc/%d/status' % (int(process)))
data.append(self.stdout)
return data
data = SSH()
while True:
print data.process_list()
几次运行后(如果不是第一次)我得到的是:
ERROR:
当我期待着:
ERROR: cat: /proc/12883/status: No such file or directory
如何确保 stderr 已准备好读取/我读取了 stderr 上的所有数据?
长话短说:我遇到了大多数此类问题,并针对大多数 ssh 相关问题提出了最终解决方案。因此请随意查看this https://stackoverflow.com/a/32758464/1729555实施exec_command
避免大多数空响应/停滞场景。
很长的故事
这里的主要问题是你exec_command()
是非阻塞的,并产生一个负责通道通信的线程。该线程正在等待传入数据并将其放入通道缓冲区,而主线程可能会继续运行。这就是你的问题所在。你太早读取缓冲区了,甚至在你检查你的一方是否收到了exit_status
。收到exit_status
确认远程进程已退出并带有给定的状态代码。接收远程状态码是not表明您已收到可能仍在传输中的所有数据。它甚至可能无序地到达你身边,说status_code
甚至可能在所有数据之前到达(stderr
,stdout
) 收到了。
当数据到达时,主线程继续。在你的情况下,主线程尝试读取stdout
and stderr
一次然后阻塞直到exit_status
准备好了。请注意,通道线程仍可能从远程命令调用接收数据。另请注意,一旦您的一方收到遥控器,您就必须手动清空缓冲区exit_status
.
在您的具体情况下,会发生以下情况:
-
execute_command
产生一个新线程(让我们调用 id执行线程、管理远程命令调用
- 虽然最近生成的线程可以接收数据,但主线程继续运行。
- [主线程] 你正在存储current的内容
stdout
,stderr
缓冲在self.stdout
,self.stderr
。请注意,您不知道是否已收到所有数据stderr
or stdout
。您很可能刚刚收到了一些块。
- [exec_thread] 愉快地接收数据
stdout
,stderr
当[主线程]继续时。
- [主线程] 繁忙阻塞,直到
exit_status
已收到。再次注意,此时您的缓冲区可能仍然充满了最近收到的stderr
,stdout
chunks.
- [主线程]
exit_status
存储在self.exit_code
。 [exec_thread] 仍然存在,可能仍然收到一些乱序数据。输入缓冲区可能仍被填满。
- [主线程] 从返回
run_cmd
请注意,该通道的 [exec_thread] - paramiko 为每个通道调用创建一个线程(即exec_command
) - 仍然存在,它们会总结闲置并产生问题,直到您手动关闭它们(stdout.channel.close()
).
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)