python are you ready_如果要检查recv_ready(),是否必须检查exit_status_ready?

也就是说,在等待recv_ready()数据准备就绪之前,我真的要先检查退出状态吗?

不。从远程进程接收数据(例如stdout/stderr)是完全正确的,即使它还没有完成。另外,一些sshd实现甚至不提供远程进程的退出状态,在这种情况下,您将遇到问题,请参见paramiko doc: exit_status_ready。

等待exit_status_code短生命远程命令的问题是,本地线程接收退出代码的速度可能比检查循环条件的速度快。在这种情况下,您永远不会进入循环,也永远不会调用readlines()。下面是一个例子:# spawns new thread to communicate with remote

# executes whoami which exits pretty fast

stdin, stdout, stderr = ssh.exec_command("whoami")

time.sleep(5) # main thread waits 5 seconds

# command already finished, exit code already received

# and set by the exec_command thread.

# therefore the loop condition is not met

# as exit_status_ready() already returns True

# (remember, remote command already exited and was handled by a different thread)

while not stdout.channel.exit_status_ready():

if stdout.channel.recv_ready():

stdoutLines = stdout.readlines()

在无限循环中等待stdout.channel.recv_ready()变为真之前,我如何知道是否应该有stdout上的数据(如果不应该有任何stdout输出,则不会这样做)?

channel.recv_ready()只表示缓冲区中有未读数据。def recv_ready(self):

"""

Returns true if data is buffered and ready to be read from this

channel. A ``False`` result does not mean that the channel has closed;

it means you may need to wait before more data arrives.

这意味着,可能由于网络(延迟数据包、重新传输,…)或只是您的远程进程没有定期写入stdout/stderr而导致recv_ready为False。因此,使用recv_ready()作为循环条件可能会导致代码过早返回,因为它完全可以在迭代中有时产生True(当远程进程写入stdout,而本地通道线程收到该输出时),有时产生False(例如,远程进程正在休眠,而不是写入stdout)。

除此之外,人们偶尔会遇到paramiko挂起,这可能与拥有stdout/stderrbuffers filling up(pot)有关。与从不读取stdout/stderr和内部缓冲区已满时的Popen和挂起进程的问题相关)。

下面的代码实现了一个分块解决方案,以便在通道打开时清空缓冲区。def myexec(ssh, cmd, timeout, want_exitcode=False):

# one channel per command

stdin, stdout, stderr = ssh.exec_command(cmd)

# get the shared channel for stdout/stderr/stdin

channel = stdout.channel

# we do not need stdin.

stdin.close()

# indicate that we're not going to write to that channel anymore

channel.shutdown_write()

# read stdout/stderr in order to prevent read block hangs

stdout_chunks = []

stdout_chunks.append(stdout.channel.recv(len(stdout.channel.in_buffer)))

# chunked read to prevent stalls

while not channel.closed or channel.recv_ready() or channel.recv_stderr_ready():

# stop if channel was closed prematurely, and there is no data in the buffers.

got_chunk = False

readq, _, _ = select.select([stdout.channel], [], [], timeout)

for c in readq:

if c.recv_ready():

stdout_chunks.append(stdout.channel.recv(len(c.in_buffer)))

got_chunk = True

if c.recv_stderr_ready():

# make sure to read stderr to prevent stall

stderr.channel.recv_stderr(len(c.in_stderr_buffer))

got_chunk = True

'''

1) make sure that there are at least 2 cycles with no data in the input buffers in order to not exit too early (i.e. cat on a >200k file).

2) if no data arrived in the last loop, check if we already received the exit code

3) check if input buffers are empty

4) exit the loop

'''

if not got_chunk \

and stdout.channel.exit_status_ready() \

and not stderr.channel.recv_stderr_ready() \

and not stdout.channel.recv_ready():

# indicate that we're not going to read from this channel anymore

stdout.channel.shutdown_read()

# close the channel

stdout.channel.close()

break # exit as remote side is finished and our bufferes are empty

# close all the pseudofiles

stdout.close()

stderr.close()

if want_exitcode:

# exit code is always ready at this point

return (''.join(stdout_chunks), stdout.channel.recv_exit_status())

return ''.join(stdout_chunks)

channel.closed只是通道过早关闭时的最终退出条件。在读取块之后,代码会检查是否已经收到退出状态,并且在此期间没有缓冲任何新数据。如果新数据到达或没有收到退出状态,代码将继续尝试读取数据块。一旦远程进程退出并且缓冲区中没有新的数据,我们假设我们已经读取了所有内容并开始关闭通道。请注意,如果您要接收退出状态,您应该一直等到收到它,否则paramiko可能会永远阻塞。

这样可以保证缓冲区不会填满并使进程挂起。exec_command仅当远程命令退出并且本地缓冲区中没有数据时返回。通过使用select()而不是在繁忙的循环中进行轮询,代码对cpu的友好程度也提高了一点,但是对于短时间的活动命令来说可能会慢一点。

仅供参考,为了防止某些无限循环,可以设置通道超时,当一段时间内没有数据到达时触发chan.settimeout(timeout)

chan.exec_command(command)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值