我有一个与用户进行交互的程序(行为类似于shell),并且我想使用Python子进程模块以交互方式运行它。这意味着,我希望可以写入标准输入并立即从标准输出中获取输出。我尝试了这里提供的许多解决方案,但似乎没有一个可以满足我的需求。
我编写的代码基于从Python内部运行交互式命令。
import Queue
import threading
import subprocess
def enqueue_output(out, queue):
for line in iter(out.readline, b''):
queue.put(line)
out.close()
def getOutput(outQueue):
outStr = ''
try:
while True: # Adds output from the queue until it is empty
outStr += outQueue.get_nowait()
except Queue.Empty:
return outStr
p = subprocess.Popen("./a.out", stdin=subprocess.PIPE, stout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize = 1)
#p = subprocess.Popen("./a.out", stdin=subprocess.PIPE, stout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True)
outQueue = Queue()
errQueue = Queue()
outThread = Thread(target=enqueue_output, args=(p.stdout, outQueue))
errThread = Thread(target=enqueue_output, args=(p.stderr, errQueue))
outThread.daemon = True
errThread.daemon = True
outThread.start()
errThread.start()
p.stdin.write("1\n")
p.stdin.flush()
errors = getOutput(errQueue)
output = getOutput(outQueue)
p.stdin.write("5\n")
p.stdin.flush()
erros = getOutput(errQueue)
output = getOutput(outQueue)
问题是队列保持为空,就像没有输出一样。仅当我将标准程序需要执行和终止的所有输入都写到标准输入中时,我才得到输出(这不是我想要的)。例如,如果我做类似的事情:
p.stdin.write("1\n5\n")
errors = getOutput(errQueue)
output = getOutput(outQueue)
有什么方法可以做我想做的事吗?
该脚本将在Linux机器上运行。我更改了脚本,并删除了Universal_newlines = True +将bufsize设置为1,并在写入后立即刷新了标准输入。仍然我没有任何输出。
第二次尝试:
我尝试了此解决方案,它对我有用:
from subprocess import Popen, PIPE
fw = open("tmpout", "wb")
fr = open("tmpout", "r")
p = Popen("./a.out", stdin = PIPE, stdout = fw, stderr = fw, bufsize = 1)
p.stdin.write("1\n")
out = fr.read()
p.stdin.write("5\n")
out = fr.read()
fw.close()
fr.close()
在Linux上针对此问题的两种解决方案:
第一个是使用文件将输出写入其中,并同时从中读取:
from subprocess import Popen, PIPE
fw = open("tmpout", "wb")
fr = open("tmpout", "r")
p = Popen("./a.out", stdin = PIPE, stdout = fw, stderr = fw, bufsize = 1)
p.stdin.write("1\n")
out = fr.read()
p.stdin.write("5\n")
out = fr.read()
fw.close()
fr.close()
第二,正如JF Sebastian提供的那样,是使用fnctl模块使p.stdout和p.stderr管道不阻塞:
import os
import fcntl
from subprocess import Popen, PIPE
def setNonBlocking(fd):
"""
Set the file description of the given file descriptor to non-blocking.
"""
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
flags = flags | os.O_NONBLOCK
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
p = Popen("./a.out", stdin = PIPE, stdout = PIPE, stderr = PIPE, bufsize = 1)
setNonBlocking(p.stdout)
setNonBlocking(p.stderr)
p.stdin.write("1\n")
while True:
try:
out1 = p.stdout.read()
except IOError:
continue
else:
break
out1 = p.stdout.read()
p.stdin.write("5\n")
while True:
try:
out2 = p.stdout.read()
except IOError:
continue
else:
break
我不完全明白你的意思。首先,我想做的是仅读取部分输出(一行)
我不明白你的榜样。该行的作用是:p = Popen([sys.executable,“ -u”,'-c''for iter(input,“”)中的行:print(“ a” * int(line)* 10 ** 6 )'],stdin = PIPE,stdout = PIPE,bufsize = 1)是什么意思?
没错,我在使用第二种解决方案时已经对其进行了编辑。问题在于,当我最初尝试该解决方案时,我已经在python解释器上进行了尝试(我没有编写脚本,只是手动对其进行了测试),因此它可以工作(由于人的响应时间很慢)。当我尝试编写脚本时,遇到了您提到的问题,因此我添加了while循环,直到输入准备就绪为止。我实际上得到的是整个输出还是什么也没有(IOError异常),如果我上传a.out源代码(这是一个C程序)会有所帮助吗?
不幸的是,我想与之通信的脚本
stty -echo
会导致'standard input': Inappropriate ioctl for device
尝试使用第一种方法时抱怨。(第二种方法似乎永远无法正确读取输出。.假设脚本正在输出)当调用一个简单的C程序以scanf(“%s”,buf)请求2个输入时,第二个解决方案将永远挂起。