Python subprocess.Popen 管道 IO 意外阻塞

Python subprocess.Popen pipe IO blocking unexpectedly

我正在尝试使用 subprocess.Popen 来控制 ssh 进程并通过管道与其交互,如下所示:

p=subprocess.Popen(['ssh','-tt','LOGIN@HOSTNAME'], stdin=subprocess.PIPE,
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                    universal_newlines=True)

while True:
    (out_stdout,out_stderr)=p.communicate(timeout=10)
    if out_stderr:
        print(out_stderr)
    if not out_stdout:
        raise EOFError
    print(out_stdout)

如果没有 ssh 的“-tt”选项,这也能正常工作。但是,如果没有分配伪 tty,我需要在 ssh 远程端与之交互的程序就会中断,所以我不得不使用它。

这似乎是 p.communicate 读取然后无限期阻塞(或直到超时),即使输入可用。

我已经使用对 io.read、select.select 等的较低级别调用重写了它,以避免经过 Popen.communicate。 Select 实际上 return 文件描述符准备就绪,但随后对该文件描述符的 io.read 也会阻塞。如果我禁用 'universal newlines' 并在 Popen 调用中设置 'bufsize=0' 那么它就可以正常工作,但是我不得不自己进行 binary/unicode 转换和行结束处理。

不过值得一提的是,在 p.communicate 版本中禁用 universal_newlines 也会无限期地阻止,因此不仅如此。

关于如何使行缓冲输入在此处正常工作而无需重新实现所有内容的任何建议?

子进程和 SSH 二进制文件的替代库更适合此类任务。

parallel-ssh:

from pssh.pssh2_client import ParalellSSHClient
client = ParallelSSHClient(['HOSTNAME'], user='LOGIN')
output = client.run_command('echo', use_pty=True)

for host, host_output in output.items():
    for line in host_output.stdout:
        print(line)

echo 替换为您需要 运行 的命令,如果不需要命令,则保持原样。即使远程端自动执行某些操作,库也要求将某些操作作为命令传入。

另请参阅同一项目的 single host SSHClient 文档。

根据文档,行解析和编码由同样跨平台的库处理。

还有其他的,如 paramiko 和 ssh2-python,它们的级别较低,需要更多代码来实现上面的等效项 - 请参阅它们各自的主页以获取示例。