标题的一部分涉及很多话题,但没有什么能完全满足整个事情。我正在远程服务器上推送一个命令,并且在很长的执行时间(比如 5 分钟左右)后需要完整的输出。使用通道我可以设置超时,但是当我读回标准输出时,我只得到了一小部分输出。解决方案似乎是等待 channel.exit_status_ready()。这适用于成功的呼叫,但失败的呼叫永远不会触发通道超时。查看文档后,我推测这是因为超时仅适用于读取操作,并且等待退出状态不符合条件。这是尝试:
channel = ssh.get_transport().open_session()
channel.settimeout(timeout)
channel.exec_command(cmd) # return on this is not reliable
while True:
try:
if channel.exit_status_ready():
if channel.recv_ready(): # so use recv instead...
output = channel.recv(1048576)
break
if channel.recv_stderr_ready(): # then check error
error = channel.recv_stderr(1048576)
break
except socket.timeout:
print("SSH channel timeout exceeded.")
break
except Exception:
traceback.print_exc()
break
漂亮,不是吗?希望它奏效。
我第一次尝试解决方案是使用 time.time() 开始,然后检查 start - time.time() > timeout。这看起来很简单,但在我目前的版本中,我输出 start - time.time() 具有应该触发中断的固定超时......并看到超时时间的两倍和三倍而没有发生中断的差异。为了节省篇幅,我会提到我的第三次尝试,我已经将它卷起来了。我在这里阅读了有关使用 select.select 等待输出的信息,并在文档中指出那里也有超时。正如您将从下面的代码中看到的那样,我混合了所有三种方法——通道超时、time.time 超时和选择超时——但仍然必须终止进程。这是弗兰肯代码:
channel = ssh.get_transport().open_session()
channel.settimeout(timeout)
channel.exec_command(cmd) # return on this is not reliable
print("{0}".format(cmd))
start = time.time()
while True:
try:
rlist, wlist, elist = select([channel], [], [],
float(timeout))
print("{0}, {1}, {2}".format(rlist, wlist, elist))
if rlist is not None and len(rlist) > 0:
if channel.exit_status_ready():
if channel.recv_ready(): # so use recv instead...
output = channel.recv(1048576)
break
elif elist is not None and len(elist) > 0:
if channel.recv_stderr_ready(): # then check error
error = channel.recv_stderr(1048576)
break
print("{0} - {1} = {2}".format(
time.time(), start, time.time() - start))
if time.time() - start > timeout:
break
except socket.timeout:
print("SSH channel timeout exceeded.")
break
except Exception:
traceback.print_exc()
break
这是一些典型的输出:
[<paramiko.Channel 3 (open) window=515488 -> <paramiko.Transport at 0x888414cL (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>], [], []
1352494558.42 - 1352494554.69 = 3.73274183273
最上面一行是 select 中的 [rlist, wlist, elist],最下面一行是 time.time() - start = (time.time() - start)。通过计算迭代次数并在循环 1000 次后在尝试的底部中断,我让这次运行中断。在示例运行中超时设置为 3。这证明我们通过了尝试,但显然,应该超时的三种方式都不起作用。
如果我从根本上误解了某些内容,请随意翻阅代码。我希望这是超级 Pythonic 并且仍在学习。