我有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.
我遇到了同样的问题.问题是退出命令后,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)
我遇到了同样的问题。
此链接(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 的通道(默认设置)。