我有一个围绕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
在do_capture()
中,只要channel.recv_ready()
告诉我可以从命令的标准输出中接收数据,我就会调用channel.recv(1024)
并将数据附加到我的缓冲区。当命令的退出状态可用时我停止。
然而,在退出状态之后的某个时刻似乎会出现更多标准数据。
# We get data after the exit status is available, why?
for i in xrange(100):
do_capture()
我不能只调用do_capture()
一次,因为看起来channel.recv_ready()
将返回False几毫秒,然后是True,接收到更多数据,然后再次返回False。
我正在使用Python 2.7.6和Paramiko 1.15.2。
答案 0 :(得分: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)
答案 1 :(得分:1)
我遇到了同样的问题。
此链接(Paramiko: how to ensure data is received between commands)给了我一些帮助,解释说在获得exit_status_ready()
之后,您仍然需要获得可能的其他数据。在我的测试中(有几个输出屏幕),在每次运行中,exit_status_ready()
返回True
后都会有其他数据要读取。
但是它读取剩余数据的方式并不正确:它使用recv_ready()
来检查是否有要读取的内容,并且一旦recv_ready()
返回False,它就会退出。现在,它大部分时间都可以使用。但是可能发生以下情况:recv_ready()
可以返回False
以指示此时没有任何内容可以接收,但这并不意味着它是所有数据的结尾。在我的测试中,我会让测试继续运行,有时需要半小时才会出现问题。
我通过阅读Channel.recv()
文档中的以下句子找到了解决方案:"If a string of length zero is returned, the channel stream has closed."
所以我们只需要一个循环并读取所有数据,直到recv()
返回零长度结果。此时关闭了频道流,但为了确保退出状态已准备好,我们可以进行额外的循环和休眠,直到channel.exit_status_ready()
返回True
。
请注意,这仅适用于未启用pty的频道(默认情况下)。