【问题标题】:Python subprocess stdout doesn't capture input promptPython子进程标准输出不捕获输入提示
【发布时间】:2020-07-28 08:10:08
【问题描述】:

我正在使用子进程生成conda create 命令并捕获生成的stdout 以供以后使用。我还立即将stdout 打印到控制台,以便用户仍然可以看到子进程的进度:

p = subprocess.Popen('conda create -n env1 python', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, b''):
    sys.stdout.write(line.decode(sys.stdout.encoding))

conda create 需要用户输入时,这可以正常工作直到执行到一半:它提示Proceed (n/y)? 并等待用户输入选项。但是,上面的代码不会打印提示,而只是等待“在空白屏幕上”输入。收到输入后,将打印提示之后,然后按预期继续执行。

我认为这是因为输入以某种方式阻止了将提示写入stdout,因此readline 直到输入块被解除后才接收新数据。

有没有办法确保在子进程等待用户输入之前打印输入提示?注意我在 Windows 上运行。

【问题讨论】:

  • 请注意,readline 实际上会查找整行,直到行尾字符 \nProceed (n/y)? y 提示确实输出整行,但需要内联输入(y)。
  • 你提出了一个很好的观点。改用read 是否可能是一个简单的修复?

标签: python subprocess conda stdout


【解决方案1】:

虽然我确信 pexpect 在这种情况下会起作用,但我认为它会是矫枉过正。相反,我使用 MisterMiyagi 的洞察力并将 readline 替换为 read

最终代码如下:

p = subprocess.Popen('conda create -n env1 python', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while p.poll() is None:
    sys.stdout.write(p.stdout.read(1).decode(sys.stdout.encoding))
    sys.stdout.flush()

请注意read(1),因为仅使用read() 会阻塞while 循环,直到找到EOF。鉴于在输入提示之前没有找到 EOF,根本不会打印任何内容!此外,调用flush 确保写入sys.stdout 的文本实际上在屏幕上可见。

【讨论】:

    【解决方案2】:

    对于这个用例,我建议使用pexpectstdin != 标准输出

    stdout

    的提示下有条件地发送到 stdin 的示例用例
    def expectgit(alog):
        TMPLOG = "/tmp/pexpect.log"
        cmd = f'''
    ssh -T git@github.com ;\
    echo "alldone" ;
    '''
        with open(TMPLOG, "w") as log:
            ch = pexpect.spawn(f"/bin/bash -c \"{cmd}\"", encoding='utf-8', logfile=log)
            while True:
                i = ch.expect(["successfully authenticated", "Are you sure you want to continue connecting"])
                if i == 0:
                    alog.info("Git all good")
                    break
                elif i == 1:
                    alog.info("Fingerprint verification")
                    ch.send("yes\r")
            ch.expect("alldone")
            i = ch.expect([pexpect.EOF], timeout=5)
            ch.close()
            alog.info("expect done - EOF")
    
        with open(TMPLOG) as log:
            for l in log.readlines():
                alog.debug(l.strip())
    

    【讨论】:

    • 这非常适合将输出显示到屏幕上。如何完成捕获用户响应以发送回子流程...?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    • 2019-12-05
    • 2020-10-18
    • 1970-01-01
    • 2011-11-28
    • 2016-07-26
    • 2012-09-17
    相关资源
    最近更新 更多