在python 3中(在Linux或MacOSX10.8上)我怎样才能让父进程只读取子进程发出的提示(不包括\n
),而不是整个缓冲区直到{{1 }}?
\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
也就是说,如何分别提取由提示和打印产生的输出?
答案 0 :(得分:2)
readline()
从names.py
读取到下一个\n
,您需要的是"尽可能多地阅读"。时间是另一个因素,因为当names.py
等待(期待输入)时,您基本上必须检测。
基本上,您需要read()
- 超时操作。您可以启动一个单独的线程,从names.py
逐字节读取到缓冲区。然后,您可以join(timeout)
此线程并访问其缓冲区。
答案 1 :(得分:0)
如果您知道提示符的外观,那么您可以使用字符串操作来获得所需的输出@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)