I have a program that interacts with the user (acts like a shell), and I want to run it using python subprocess module interactively. That means, I want the possibility to write to stdin and immediately get the output from stdout. I tried many solutions offered here, but none of them seems to work for my needs.
The code I've written based on Running an interactive command from within 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)
The problem is that the queue remains empty, as if there is no output. Only if I write to stdin all the input that the program needs to execute and terminate, then I get the output (which is not what I want). For example if I do something like:
p.stdin.write("1\n5\n")
errors = getOutput(errQueue)
output = getOutput(outQueue)
Is there any way to do what I want to do?
EDIT: The script will run on a Linux machine. I changed my script and deleted the universal_newlines=True + set the bufsize to 1 and flushed stdin immediately after wrtie. Still I get no output.
Second try: I tried this solution and it works for me:
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()
Two solutions for this issue on Linux:
First one is to use a file to write the output to, and read from it simultaneously:
Second, as J.F. Sebastian offered, is to make p.stdout and p.stderr pipes non-blocking using fnctl module:
None of the current answers worked for me. At the end, I've got this working:
Tested with this
dummy.py
script:The caveats are: input/output always lines with newline, flushing child's stdin after every write, and use
readline()
from child's stdout (all of that managed in the functions).It's a pretty simple solution, in my opinion (not mine, I found it here: https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/). I was using Python 3.6.
Here is an interactive shell. You have to run read() on a separate thread, otherwise it will block the write()