Capture the standard from the Paramiko team

I have a wrapper around Paramiko SSHClient.exec_command() . I would like to get the standard version. Here's a shortened version of my function:

 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 

In do_capture() , when channel.recv_ready() tells me that I can get data from the stdout command, I call channel.recv(1024) and add the data to my buffer. I stop when the exit status of the command is available.

However, it seems that more stdout data appears at some point after the exit status.

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

I cannot just call do_capture() once, since it seems that channel.recv_ready() will return False in a few milliseconds and then True, and more data will be received, and then False again.

I am using Python 2.7.6 with Paramiko 1.15.2.

+3
source share
2 answers

I ran into the same problem. The problem is that after the command exits, there may still be data on the stout or stderr buffers that are still on the network path or something else. I read the paramiko source code and apparently all the data was read as soon as chan.recv () returns an empty string.

So, this is my attempt to solve this problem while it does not work.

 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 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) 
+4
source

I ran into the same problem.

This link ( Paramiko: how to ensure that data is received between the teams ) helped me explain that after receiving exit_status_ready() you still have to receive possible additional data. In my tests (with several output screens) in each separate run additional data will be read after exit_status_ready() returns True .

But as he reads the remaining data, this is not true: he uses recv_ready() to check if there is something to read, and as soon as recv_ready() returns False, it will exit. Now it will work most of the time. But the following situation may occur: recv_ready() can return False to indicate that at that moment there is nothing to receive, but this does not mean that this is the end of all data. In my tests, I would leave the test running, and sometimes it takes half an hour for the problem to appear.

I found a solution by reading the following sentence in the Channel.recv() documentation: "If a string of length zero is returned, the channel stream has closed."

Thus, we can have only one cycle and read all the data until recv() returns a result with zero length. At this moment, the channel stream is closed, but to make sure that the output state is ready, we can do an additional loop and sleep mode until channel.exit_status_ready() returns True .

Please note that this will only work with a channel without pty enabled (which is the default).

+1
source

All Articles