如何确保命令之间接收到数据

7

我正在使用Paramiko来执行一些命令并收集结果以进行进一步的分析。偶尔第一个命令的结果没有及时返回,就会出现在第二个命令的输出中。

我尝试使用recv_ready来解决这个问题,但它没有起作用,所以我认为我做错了什么。以下是相关代码:

pause = 1

def issue_command(chan, pause, cmd):
    # send commands and return results
    chan.send(cmd + '\n')
    while not chan.recv_ready():
        time.sleep(pause)
    data = chan.recv(99999)

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
chan = ssh.connect(host, port=22, username=username, password=password, timeout=3,)

resp1 = issue_command(chan, pause, cmd1)
resp2 = issue_command(chan, pause, cmd2)

这些命令的输出内容相对较少(几句话而已)。增加暂停时间可能会解决问题,但并不是理想的解决方案。
2个回答

11

我会直接使用transport并为每个命令创建一个新的通道。然后你可以使用如下代码:

def issue_command(transport, pause, command):
    chan = transport.open_session()
    chan.exec_command(command)

    buff_size = 1024
    stdout = ""
    stderr = ""

    while not chan.exit_status_ready():
        time.sleep(pause)
        if chan.recv_ready():
            stdout += chan.recv(buff_size)

        if chan.recv_stderr_ready():
            stderr += chan.recv_stderr(buff_size)

    exit_status = chan.recv_exit_status()
    # Need to gobble up any remaining output after program terminates...
    while chan.recv_ready():
        stdout += chan.recv(buff_size)

    while chan.recv_stderr_ready():
        stderr += chan.recv_stderr(buff_size)

    return exit_status, stdout, stderr

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, port=22, username=username, password=password, timeout=3,)
transport = ssh.get_transport()
pause = 1    

resp1 = issue_command(transport, pause, cmd1)
resp2 = issue_command(transport, pause, cmd2)

更好的方法是针对一组命令创建新的通道,轮询每个通道的 recv_ready,并在输出可用时收集其stdout/stderr。:-)

编辑:在命令退出后读取数据可能存在潜在问题。请参阅评论!


2
请注意,此解决方案可行但并非万无一失 - 在所有数据接收完成之前,它仍可能退出,因为 recv_ready() 可能在通道流关闭之前返回 False。更多详情请参见:https://dev59.com/uIzda4cB1Zd3GeqPkUQT#39019370 - Ivan
你的代码存在竞态条件:在命令退出并且你已经检索到退出状态之后,stdout或stderr缓冲区中可能仍然存在数据。请参见:https://dev59.com/uIzda4cB1Zd3GeqPkUQT#39918539 - Patrick

0

我喜欢@ferrouswheel的答案。 我一直在使用与@KMS类似的解决方案,即:

def wait_time():
    while True:
        if chan.recv_ready() == False:
            time.sleep(1)
            continue
        else:
            break

这也可以缩短为:

def wait_time():
    while chan.recv_ready() == False:
        time.sleep(1)

无论哪种情况,每当需要时,我只需放置:

wait_time()

..在代码内部。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接