python - 标准错误上的 Paramiko recv()/read()/readline(s)() 返回空字符串

标签 python ssh paramiko stderr

我正在使用 paramiko 收集有关远程主机的一些信息并遇到问题,在读取 (read()/readline()/readlines( )) 来自 stderr channel 。

有时 stderr.read() 会返回一个空字符串,对我来说这看起来像是竞争条件的结果。 但是,根据我在 Internet 上找到的文档和示例,这似乎是正确的方法。

我还尝试打开一个专用 channel 并利用 chan.recv_ready()/chan.recv_stderr_ready() 并通过循环从各个 channel 读取 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()

经过几次运行(如果不是第一次)我得到的是: 错误: 虽然我期待: 错误:cat:/proc/12883/status:没有那个文件或目录

如何确保 stderr 已准备好读取/我读取了 stderr 上的所有数据?

最佳答案

长话短说:我遇到过这些问题中的大多数,并且针对我的大多数 ssh 相关问题提出了最终解决方案。因此,请随时查看 this exec_command 的实现避免了大多数 empty_response/stalling 场景。

说来话长

这里的主要问题是您的 exec_command() 是非阻塞的,并且生成一个负责 channel 通信的线程。该线程正在等待传入数据并将其放入 channel 缓冲区,而主线程可能会继续运行。这就是你的问题所在。您读取缓冲区的时间过早,甚至在您检查您的一方是否收到 exit_status 之前。收到 exit_status 确认远程进程以给定的状态代码退出。收到远程状态代码并不表示您已收到可能仍在传输中的所有数据。它甚至可能乱序到达你身边,说 status_code 可能甚至在所有数据之前到达(stderr,stdout)收到了。

当数据到达时,主线程继续。在您的情况下,主线程尝试从 stdoutstderr 读取一次,然后阻塞直到 exit_status 准备就绪。请注意, channel 线程可能仍会从您的远程命令调用接收数据。另请注意,一旦您的一方收到远程 exit_status,您将必须手动清空缓冲区。

在您的特定情况下,会发生这种情况:

  1. execute_command 生成一个新线程(调用 id exec_thread,管理远程命令调用
  2. 虽然最近生成的线程可能会收到数据,但主线程会继续运行。
  3. [main-thread] 您正在将stdoutstderr 缓冲区的当前 内容存储在self.stdout,self.stderr。请注意,您不知道是否收到了 stderrstdout 的所有数据。您很可能刚刚收到了一些 block 。
  4. [exec_thread] 愉快地为 stdoutstderr 接收数据,而 [main-thread] 继续。
  5. [main-thread] 忙阻塞,直到收到 exit_status。再次注意,此时您的缓冲区可能仍然充满了最近收到的 stderrstdout block 。
  6. [main-thread] exit_status 存储在self.exit_code 中。 [exec_thread] 仍然存在,可能仍会收到一些乱序数据。输入缓冲区可能仍处于填充状态。
  7. [主线程] 从 run_cmd 返回

请注意,该 channel 的 [exec_thread] - paramiko 为每个 channel 调用创建一个线程(即 exec_command) - 仍然存在,它们会一直闲置并产生问题,直到您手动关闭它们(stdout.channel.close()).

关于python - 标准错误上的 Paramiko recv()/read()/readline(s)() 返回空字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33708336/

相关文章:

python - 如何使用 Paramiko 下载昨天的文件?

python - 如何查询设置每月的帖子

python - 等到任务通过 Python 在远程机器上完成

python - 如何在 Python 中创建全零数据框

node.js - 在nodejs中使用ssh2实现scp功能

amazon-web-services - (google-chrome:23461):Gtk警告**:12:34:29.640:无法打开显示:

heroku - 使用端口 443 连接到 Heroku

python - Paramiko 关闭连接不起作用

python - 在python中创建一个带有初始值的md5对象

python - 使用 AWS Chalice 生成 PDF