干活时发现一个问题,通过以下方式建立一个SSH客户端后:
self.ssh = paramiko.SSHClient()
当我们利用这个SSH去执行命令时:
stdout = self.ssh.exec_command(cmd, timeout, get_pty=True)[1]
此处的cmd指的是要在SSH连上的远程主机操作的命令;
此处的timeout指的是底层 SSH 操作的超时设置,因此不能直接用来对cmd进行一个执行时间的限制。但查资料后发现,有一种办法可以解决这个问题:
stdout = self.ssh.exec_command(cmd, timeout, get_pty=True)[1]
result = stdout.readlines()
在获取stdout后,马上执行一个readlines操作。
这个readlines会去读取stdout来制作列表,如果exec_command在timeout时间内未完成cmd的执行,此时的stdout会是错乱的,因此对stdout进行readlines会直接抛出异常!此时只需要对异常进行相应处理就可以完成想要的效果。 相反,如果是cmd在timeout内执行完成的话,readlines就不会有异常抛出~
举例:
stdout = self.ssh.exec_command(cmd, timeout, get_pty=True)[1]
try:
result = stdout.readlines()
self.logger.info(result)
except Exception as e:
self.logger.error(f'Timeout while executing command: {e}')
return errno.ETIME
事实证明,任何对stdout做处理的方式都可以在超时后触发异常:
stdout = self.ssh.exec_command(cmd, timeout, get_pty=True)[1]
try:
output = stdout.read().decode('utf-8')
self.logger.info(output)
except Exception as e:
self.logger.error(f'Timeout while executing command: {e}')
return errno.ETIME
=========================================================================
原本问题到这里就结束了,但是在调整代码时又发现了一个问题:
如果我执行代码如下:
stdout = self.ssh.exec_command(cmd, timeout, get_pty=True)[1]
exit_stat = stdout.channel.recv_exit_status()
result = stdout.readlines()
在readlines之前添加一行recv_exit_status,就会导致不管exec_command时有没有timeout都不会报错。查资料后发现,当执行recv_exit_status时,不管exec_command的执行状态如何,都会关闭ssh channel通道,即使远程命令尚未执行完毕关闭,且exit_stat都为0(非异常状态)。因此即使远程命令尚未执行完毕,也不会导致readlines()报错,因为通道已经关闭了。这样我们原本建立的 "exec_command->马上抓取stdout状态" 的方式就失效了~