Warm tip: This article is reproduced from serverfault.com, please click

subprocess-使用Python的交互式输入/输出

(subprocess - Interactive input/output using Python)

发布于 2013-11-09 17:57:45

我有一个与用户进行交互的程序(行为类似于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()
Questioner
Talor Abramovich
Viewed
11
69.4k 2016-01-16 09:50:32

在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