Question
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)
How-To
Second try: I tried this solution and it works for me. Firstly, a testing shell to echo with input line:
- test.sh
- #!/bin/sh
- while read LINE; do
- echo "OUT> $LINE"
- if [ "$LINE" == "bye" ]; then
- break
- fi
- done
Then we can use python code to run this interactive shell:
- test.py
- from subprocess import Popen, PIPE
- import time
- class CmdInteractor:
- def __init__(self, cmd, tmp_file = "tmpout"):
- self.cmd = cmd
- self.fw = open(tmp_file, "wb")
- self.fr = fr = open(tmp_file, "r")
- self.p = Popen(cmd, shell=True, stdin = PIPE, stdout = self.fw, stderr = self.fw)
- def sendline(self, msg):
- self.p.stdin.write(("%s\n" % (msg)).encode()); self.p.stdin.flush()
- def readline(self, timeout=10):
- r'''
- Read one line from stdout
- @param timeouot:
- Timeout in second
- '''
- out = self.fr.readline()
- wi = 0
- while out is None or out == '':
- wi = wi + 1
- if wi > 10:
- raise Exception('Timeout in reading line from output!')
- time.sleep(1)
- out = self.fr.readline()
- return out.rstrip()
- def __del__(self):
- self.fw.close()
- self.fr.close()
- self.p.terminate()
- ci = CmdInteractor('./test.sh')
- ci.sendline('Test')
- print("Read: %s" % (ci.readline()))
- ci.sendline('Hello')
- print("Read: %s" % (ci.readline()))
- print("\t[Info] Say bye!")
- ci.sendline('bye')
- print("Read: %s" % (ci.readline()))
Supplement
* Running an interactive command from within python
* Python std library - subprocess — Subprocess management
沒有留言:
張貼留言