繁体   English   中英

仅在 Python subprocess.Popen.communicate 中发送 stdin 后才获取 stdout

[英]Getting stdout only after stdin sent in Python subprocess.Popen.communicate

我正在尝试使用 Python 中的subprocess模块编写一些简单的测试。 被测试的程序很简单:

def main():
    x = int(input("Integer? "))
    print('Output is', x // 12)


main()

为了测试它,我正在调用这个函数:

def test_output():
    ret = subprocess.Popen(args=['python3', FILENAME],
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)
    output, err = ret.communicate(b"216\n")
    print(f"Output: {output}")

但是, output同时捕获来自目标程序的input调用的提示以及随后的标准输出:

Output: b'Integer? Output is 18\n'

我怎样才能得到Output is 18\\n'部分? 我不在乎input()的提示

更糟糕的方法:等待提示

在这里,我们在阻塞模式下读取一个字节(延迟直到打印提示),然后在切换回阻塞模式之前消耗准备好的其他所有内容并在非阻塞模式下等待读取。

#!/usr/bin/env python3

from select import select
import fcntl
import subprocess
import threading
import os
import sys

def test_output(timeout=0.1):
    ret = subprocess.Popen(args=['python3', 'testsubject.py'],
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE,
                           bufsize=0)
    # you probably want to replace this with a function that _stores_ stderr
    stderr_reader = threading.Thread(target = ret.stderr.read)
    stderr_reader.start()
    # wait until the prompt has been at least partially written
    prompt_start = ret.stdout.read(1)
    # go into nonblocking mode
    orig_flags = fcntl.fcntl(ret.stdout, fcntl.F_GETFL)
    fcntl.fcntl(ret.stdout, fcntl.F_SETFL, orig_flags | os.O_NONBLOCK)
    # read further output until there's nothing left
    prompt_rest = ret.stdout.read()
    print(f"Skipping prompt: {prompt_start.decode('utf-8')}{prompt_rest.decode('utf-8')}", file=sys.stderr)
    # exit nonblocking mode
    fcntl.fcntl(ret.stdout, fcntl.F_SETFL, orig_flags)
    # write to stdin
    ret.stdin.write(b'216\n')
    ret.stdin.close()
    output = ret.stdout.read()
    print(f"Output: {output.decode('utf-8')}")

test_output()

更好的方法:修复被测程序

此处出错的组件是作为子进程调用的程序,而不是调用子进程的程序。 有两种主要方法可以修复它:

  1. 当用户不以交互方式运行时,不要打印提示。

     def main(): x = int(input("Integer? " if os.isatty(0) else None)) print('Output is', x // 12) main()

    这是有效的,因为当程序使用stdin=subprocess.PIPE os.isatty(0)运行时os.isatty(0)为假。

  2. 将提示打印到 stderr,而不是 stdout,因此它与“诊断日志”(“准备好输入”是诊断状态的一个示例)分开,而不是与输出分开。

     def main(): sys.stderr.write("Integer? ") x = int(input()) print('Output is', x // 12) main()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM