Python subprocess.Popen管道IO意外阻塞

Python subprocess.Popen管道IO意外阻塞,python,linux,python-3.x,ssh,pty,Python,Linux,Python 3.x,Ssh,Pty,我试图使用subprocess.Popen控制ssh进程并通过管道与之交互,如下所示: p=subprocess.Popen(['ssh','-tt','LOGIN@HOSTNAME'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) while True: (o

我试图使用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实际上会将文件描述符返回为就绪状态,但对该文件描述符的后续io.read也会阻塞。如果我禁用“通用换行符”并在Popen调用中设置“bufsize=0”,那么它可以正常工作,但随后我不得不自己进行二进制/unicode转换和行尾处理

不过值得一提的是,在p.communicate版本中禁用universal_新行也会无限期地阻塞,所以不仅仅如此


关于如何让行缓冲输入在这里正常工作而不必重新实现所有内容,有什么建议吗?

对于此类任务,可以使用子流程和SSH二进制文件的库替代方案

:

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

有关同一项目的详细信息,请参见文档

根据文档,行解析和编码由库处理,库也是跨平台的


还有一些像paramiko和ssh2-python这样的较低级别的应用程序,需要更多的代码来实现上述等效功能,请参见它们各自的主页以获取示例。

您是否可以在一个打包了expect to Simulation a TTY的实用程序下运行远程程序?顺便说一句,一般来说,这是运行远程SSH进程的错误方式。使用本机库的痛苦要小得多-这样,您就可以在客户端和服务器之间只传输一个经过身份验证的连接,并为要运行的每个程序创建一个新通道,获取单独的stdout/stderr流和每个流的退出状态,而不需要尝试猜测shell输出的组成、不同远程程序之间的边界等。如果使用ControlMaster/ControlSocket功能,OpenSSH命令行可以完成这些任务,但这比使用一个干净地公开底层功能的本机Python库要痛苦得多。@CharlesDuffy遗憾的是没有。远程程序在登录时自动启动,我没有权限让它执行任何其他操作。我真的需要看一台复制机,以便能够想出一个我对此充满信心的修复方案。如果您对远程程序所做的事情有足够的了解,可以提供一个脚本供用户在远程端安装和测试,这将使您更容易提供明确的答案。谢谢。这确实工作得更好,而且相当轻。让它与特定的应用程序一起工作有一些问题,但它确实解决了行缓冲io的问题。如果有人有任何想法,我仍然很想知道在我的原始示例中到底发生了什么。我知道数据已经准备好读取,并且包含一行新行,所以这些读取应该会返回。但事实并非如此。为什么在另一端有一个pty会产生任何不同,这也是一个谜。pty确实很重要。除了控制行缓冲外,它还将换行符从\r\n更改为\r\n。这就是需要通用换行符的原因,也可能是select无法按预期工作的原因。即便如此,我不确定它是否会正确认识到,即使在posix操作系统上,它也应该使用windows风格的换行符进行解析。无论哪种方式,您都需要在子流程上直接使用.stdout。
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)