【问题标题】:Python 3 detailed control of I/O of another processPython 3 详细控制另一个进程的 I/O
【发布时间】:2013-01-09 17:53:20
【问题描述】:

在 python 3(在 Linux 或 MacOSX10.8 上)我怎样才能让父进程只读取由子进程发出的提示(不包括 \n),而不是直到 \n 之前的整个缓冲区?

# program names.py
print("I am Joe.")                   #1print
name = input("What is your name? ")  #2prompt
print("Hi", name, "!")               #3print

# program parent.py (in python 3.3.0)
import subprocess
import sys
p = subprocess.Popen([sys.executable, "names.py"],
                      bufsize=0,
                      stdin =subprocess.PIPE,
                      stdout=subprocess.PIPE,
                      stderr=subprocess.PIPE,
                      shell=False,
                      universal_newlines=True
                    )
print(p.stdout.readline(), end='')
p.stdin.write("Sue\n")
# The next print will produce the output of 2prompt + 3print:
print(p.stdout.readline(), end='')

当我运行 parent.py 时,它会打印:

I am Joe.
What is your name? Hi Sue !

如何修改parent.py 以便打印:

I am Joe.
What is your name? 
Hi Sue !

即如何分别提取prompt和print产生的输出?

【问题讨论】:

  • 可能不是您所需要的,但您可以在“Hi”的开头打印一个换行符...

标签: python io subprocess pipe python-3.3


【解决方案1】:

readline()names.py 读取直到下一个\n,您需要的是“阅读尽可能多的内容”。时间是这里的一个额外因素,因为您基本上必须检测names.py 何时等待(期望输入)。

基本上,您需要一个带有超时的read()-操作。您可以启动一个单独的线程,将names.py 逐字节读取到缓冲区中。然后你可以join(timeout)这个线程并访问它的缓冲区。

【讨论】:

【解决方案2】:

如果您知道提示的外观,那么您可以使用字符串操作来获得所需的输出为@Michael suggested

import sys
from subprocess import Popen, PIPE

p = Popen([sys.executable, "names.py"], stdin=PIPE, stdout=PIPE,
          universal_newlines=True)
output = p.communicate("Sue\n")[0]
prompt = "name? "
print(output.replace(prompt, prompt + "\n"))

输出

I am Joe.
What is your name? 
Hi Sue !

如果您不知道提示的外观,那么如果子进程在非交互式运行时使用块缓冲,the timeout-based solution suggested by @Exp 可能不起作用。虽然它确实适用于names.py。这是一个基于超时的解决方案,它使用select 而不是线程来读取输出:

import os
import sys
from select import select
from subprocess import Popen, PIPE

timeout = 1 # seconds
with Popen([sys.executable, 'names.py'],
           stdin=PIPE, stdout=PIPE,  bufsize=0) as p:
    while True:
        ready = select([p.stdout], [], [], timeout)[0]
        if ready: # there is something to read
            data = os.read(p.stdout.fileno(), 512)
            if not data: # EOF
                break
            sys.stdout.buffer.write(data) # echo subprocess output
        elif p.poll() is None: # timeout, but subprocess is still running
            print("") # print newline after the prompt
            p.stdin.write(b"Sue\n") # answer the prompt
        else: # subprocess exited
            break

它在延迟后产生与第一个代码示例相同的输出。

一般来说,pexpect 可用于模拟子进程的交互模式。

如果你知道提示的样子:

import sys
import pexpect

print(pexpect.run(sys.executable + " -mnames", events={r'name\? ': 'Sue\n'}))
# note: it echos our answer too (it can be avoided if necessary)

输出

I am Joe.
What is your name? Sue
Hi Sue !

这是一个基于超时的解决方案,可以避免回显答案:

import sys
import pexpect # pip install pexpect-u

child = pexpect.spawn(sys.executable + " -mnames", timeout=1)
child.logfile_read = sys.stdout # echo subprocess output
child.expect(pexpect.TIMEOUT)
print("") # print newline after the prompt
child.setecho(False) # don't echo our answer
child.sendline('Sue')
child.expect(pexpect.EOF)
child.close()

要使用subprocess 重现它,可以使用pty 模块:

import os
import pty
import sys
from select import select
from subprocess import Popen, STDOUT

timeout = 1 # seconds
master_fd, slave_fd = pty.openpty()
with Popen([sys.executable, 'names.py'],
           stdin=slave_fd, stdout=slave_fd, stderr=STDOUT,
           bufsize=0) as p:
    while True:
        ready = select([master_fd], [], [], timeout)[0]
        if ready: # there is something to read
            data = os.read(master_fd, 512)
            if not data: # EOF
                break
            sys.stdout.buffer.write(data) # echo subprocess output
        elif p.poll() is None: # timeout, but subprocess is still running
            # assume that child process waits for input after printing the prompt
            answer = b"Sue\n"
            os.write(master_fd, answer) # asnwer the prompt
            os.read(master_fd, len(answer)) # don't echo our answer
        else: # subprocess exited
            break
    os.close(slave_fd)
    os.close(master_fd)

【讨论】:

    猜你喜欢
    • 2011-08-21
    • 2023-03-17
    • 1970-01-01
    • 2015-01-29
    • 1970-01-01
    • 1970-01-01
    • 2017-01-13
    • 1970-01-01
    • 2012-03-08
    相关资源
    最近更新 更多