从Paramiko命令中捕获标准

rgo*_*gov 4 python paramiko

我有Paramiko的包装纸SSHClient.exec_command().我想捕捉标准.这是我的函数的缩短版本:

def __execute(self, args, sudo=False, capture_stdout=True, plumb_stderr=True,
  ignore_returncode=False):

  argstr = ' '.join(pipes.quote(arg) for arg in args)

  channel = ssh.get_transport().open_session()
  channel.exec_command(argstr)

  channel.shutdown_write()

  # Handle stdout and stderr until the command terminates
  captured = []

  def do_capture():
    while channel.recv_ready():
      o = channel.recv(1024)
      if capture_stdout:
        captured.append(o)
      else:
        sys.stdout.write(o)
        sys.stdout.flush()

    while plumb_stderr and channel.recv_stderr_ready():
      sys.stderr.write(channel.recv_stderr(1024))
      sys.stderr.flush()

  while not channel.exit_status_ready():
    do_capture()

  # We get data after the exit status is available, why?
  for i in xrange(100):
    do_capture()

  rc = channel.recv_exit_status()
  if not ignore_returncode and rc != 0:
    raise Exception('Got return code %d executing %s' % (rc, args))

  if capture_stdout:
    return ''.join(captured)

paramiko.SSHClient.execute = __execute
Run Code Online (Sandbox Code Playgroud)

do_capture(),每当channel.recv_ready()告诉我我可以从命令的stdout接收数据时,我调用channel.recv(1024)并将数据附加到我的缓冲区.当命令的退出状态可用时我停止.

但是,在退出状态之后的某个时刻似乎会出现更多标准数据.

# We get data after the exit status is available, why?
for i in xrange(100):
  do_capture()
Run Code Online (Sandbox Code Playgroud)

我不能只调用do_capture()一次,因为它似乎channel.recv_ready()将返回False几毫秒,然后是True,接收更多数据,然后再次返回False.

我正在使用Python 2.7.6和Paramiko 1.15.2.

Pat*_*ick 5

我遇到了同样的问题.问题是退出命令后,stout或stderr缓冲区上仍可能存在数据,仍然在网络上或其他任何地方.我读了paramiko的源代码,显然所有数据都被读过一次chan.recv()返回空字符串.

所以这是我尝试解决它,直到现在它一直在工作.

def run_cmd(ssh, cmd, stdin=None, timeout=-1, recv_win_size=1024):
    '''
    Run command on server, optionally sending data to its stdin

    Arguments:
        ssh           -- An instance of paramiko.SSHClient connected
                         to the server the commands are to be executed on
        cmd           -- The command to run on the remote server
        stdin         -- String to write to command's standard input
        timeout       -- Timeout for command completion in seconds.
                         Set to None to make the execution blocking.
        recv_win_size -- Size of chunks the output is read in

    Returns:
        A tuple containing (exit_status, stdout, stderr)
    '''

    with closing(ssh.get_transport().open_session()) as chan:
        chan.settimeout(timeout)
        chan.exec_command(cmd)
        if stdin:
            chan.sendall(stdin)
            chan.shutdown_write()

        stdout, stderr = [], []

        # Until the command exits, read from its stdout and stderr
        while not chan.exit_status_ready():
            if chan.recv_ready():
                stdout.append(chan.recv(recv_win_size))
            if chan.recv_stderr_ready():
                stderr.append(chan.recv_stderr(recv_win_size))

        # Command has finished, read exit status
        exit_status = chan.recv_exit_status()

        # Ensure we gobble up all remaining data
        while True:
            try:
                sout_recvd = chan.recv(recv_win_size)
                if not sout_recvd and not chan.recv_ready():
                    break
                else:
                    stdout.append(sout_recvd)
            except socket.timeout:
                continue

        while True:
            try:
                serr_recvd = chan.recv_stderr(recv_win_size)
                if not serr_recvd and not chan.recv_stderr_ready():
                    break
                else:
                    stderr.append(serr_recvd)
            except socket.timeout:
                continue

    stdout = ''.join(stdout)
    stderr = ''.join(stderr)

    return (exit_status, stdout, stderr)
Run Code Online (Sandbox Code Playgroud)


Iva*_*van 1

我遇到了同样的问题。

此链接(Paramiko:如何确保在命令之间接收数据)给了我一些帮助,解释了在获得之后exit_status_ready()您仍然必须接收可能的附加数据。在我的测试中(有几个输出屏幕),在每次运行中,在exit_status_ready()returns后都会有额外的数据需要读取True

但它读取剩余数据的方式是不正确的:它用来recv_ready()检查是否有东西要读取,一旦recv_ready()返回 False 就退出。现在,它在大多数情况下都可以工作。但可能会出现以下情况:recv_ready()can returnFalse表示此时没有任何数据可接收,但并不意味着此时是所有数据的结束。在我的测试中,我会让测试继续运行,有时需要半个小时才会出现问题。

我通过阅读文档中的以下句子找到了解决方案Channel.recv()"If a string of length zero is returned, the channel stream has closed."

所以我们可以有一个循环并读取所有数据,直到recv()返回零长度结果。此时通道流已关闭,但为了确保退出状态已准备好,我们可以进行额外的循环并睡眠直到channel.exit_status_ready()returns True

请注意,这仅适用于未启用 pty 的通道(默认设置)。