![](/img/trans.png)
[英]python subprocess.Popen.communicate() closes p.stdout and p.stderr files, but where?
[英]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()
此处出错的组件是作为子进程调用的程序,而不是调用子进程的程序。 有两种主要方法可以修复它:
当用户不以交互方式运行时,不要打印提示。
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)
为假。
将提示打印到 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.