2017年5月12日 星期五

[ Python 常見問題 ] Interactive input/output using python

Source From Here 
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
  1. import Queue  
  2. import threading  
  3. import subprocess  
  4.   
  5. def enqueue_output(out, queue):  
  6.     for line in iter(out.readline, b''):  
  7.         queue.put(line)  
  8.     out.close()  
  9.   
  10. def getOutput(outQueue):  
  11.     outStr = ''  
  12.     try:  
  13.         while True: #Adds output from the Queue until it is empty  
  14.             outStr+=outQueue.get_nowait()  
  15.   
  16.     except Queue.Empty:  
  17.         return outStr  
  18.   
  19. p = subprocess.Popen("./a.out", stdin=subprocess.PIPE, stout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize = 1)  
  20. #p = subprocess.Popen("./a.out", stdin=subprocess.PIPE, stout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True)  
  21.   
  22. outQueue = Queue()  
  23. errQueue = Queue()  
  24.   
  25. outThread = Thread(target=enqueue_output, args=(p.stdout, outQueue))  
  26. errThread = Thread(target=enqueue_output, args=(p.stderr, errQueue))  
  27.   
  28. outThread.daemon = True  
  29. errThread.daemon = True  
  30.   
  31. outThread.start()  
  32. errThread.start()  
  33.   
  34. p.stdin.write("1\n")  
  35. p.stdin.flush()  
  36. errors = getOutput(errQueue)  
  37. output = getOutput(outQueue)  
  38.   
  39. p.stdin.write("5\n")  
  40. p.stdin.flush()  
  41. erros = getOutput(errQueue)  
  42. 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 ). Is there any way to do what I want to do? 

How-To 
Second try: I tried this solution and it works for me. Firstly, a testing shell to echo with input line: 
- test.sh 
  1. #!/bin/sh  
  2. while read LINE; do  
  3.     echo "OUT> $LINE"  
  4.     if [ "$LINE" == "bye" ]; then  
  5.         break  
  6.     fi  
  7. done  
Usage: 
# ./test.sh
test line1
OUT> test line1
test line2
OUT> test line2
bye // Exit the interactive shell
OUT> bye

Then we can use python code to run this interactive shell: 
- test.py 
  1. from subprocess import Popen, PIPE  
  2. import time  
  3.   
  4.   
  5. class CmdInteractor:  
  6.     def __init__(self, cmd, tmp_file = "tmpout"):  
  7.         self.cmd = cmd  
  8.         self.fw = open(tmp_file, "wb")  
  9.         self.fr = fr = open(tmp_file, "r")  
  10.         self.p = Popen(cmd, shell=True, stdin = PIPE, stdout = self.fw, stderr = self.fw)  
  11.   
  12.     def sendline(self, msg):  
  13.         self.p.stdin.write(("%s\n" % (msg)).encode()); self.p.stdin.flush()  
  14.   
  15.     def readline(self, timeout=10):  
  16.         r'''  
  17.         Read one line from stdout  
  18.   
  19.         @param timeouot:  
  20.             Timeout in second  
  21.         '''  
  22.         out = self.fr.readline()  
  23.         wi = 0  
  24.         while out is None or out == '':  
  25.             wi = wi + 1  
  26.             if wi > 10:  
  27.                 raise Exception('Timeout in reading line from output!')  
  28.             time.sleep(1)  
  29.             out = self.fr.readline()  
  30.         return out.rstrip()  
  31.   
  32.     def __del__(self):  
  33.         self.fw.close()  
  34.         self.fr.close()  
  35.         self.p.terminate()  
  36.   
  37. ci = CmdInteractor('./test.sh')  
  38. ci.sendline('Test')  
  39. print("Read: %s" % (ci.readline()))  
  40. ci.sendline('Hello')  
  41. print("Read: %s" % (ci.readline()))  
  42. print("\t[Info] Say bye!")  
  43. ci.sendline('bye')  
  44. print("Read: %s" % (ci.readline()))  
Execution out: 
# ./test.py
Read: OUT> Test
Read: OUT> Hello
[Info] Say bye!
Read: OUT> bye

Supplement 
Running an interactive command from within python 
Python std library - subprocess — Subprocess management 

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...