0

I am working on a python program which implements the cmd window. I am using subproccess with PIPE. If for example i write "dir" (by stdout), I use communicate() in order to get the response from the cmd and it does work.

The problem is that in a while True loop, this doesn't work more than one time, it seems like the subprocess closes itself.. Help me please

import subprocess
process = subprocess.Popen('cmd.exe', shell=False, stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=None)
x=""
while x!="x":
    x = raw_input("insert a command \n")
    process.stdin.write(x+"\n")
    o,e=process.communicate()
    print o

process.stdin.close()
Guy
  • 91
  • 1
  • 6
  • Can you post the relevant code? – stevieb Jun 05 '15 at 19:03
  • Sure.. I am updating – Guy Jun 05 '15 at 19:25
  • @Guy: [`Popen.communicate()` documentation](https://docs.python.org/2/library/subprocess.html#subprocess.Popen.communicate) says: *"Wait for process to terminate."* -- it means that you can call it at most once per child process. – jfs Jun 05 '15 at 20:17
  • That's the problem.. Could you help me make it stay alive? because if I have commands like cd.. I would like it to stay.. not to close and open a new one.. – Guy Jun 05 '15 at 21:24
  • [Nonblocking IO is difficult with subprocess](http://stackoverflow.com/questions/375427/non-blocking-read-on-a-subprocess-pipe-in-python). Also checkout [winpexpect](https://bitbucket.org/geertj/winpexpect/wiki/Home) – Bryce Guinta Jun 05 '15 at 22:56

1 Answers1

1

The main problem is that trying to read subprocess.PIPE deadlocks when the program is still running but there is nothing to read from stdout. communicate() manually terminates the process to stop this.

A solution would be to put the piece of code that reads stdout in another thread, and then access it via Queue, which allows for reliable sharing of data between threads by timing out instead of deadlocking.

The new thread will read standard out continuously, stopping when there is no more data.

Each line will be grabbed from the queue stream until a timeout is reached(no more data in Queue), then the list of lines will be displayed to the screen.

This process will work for non-interactive programs

import subprocess
import threading
import Queue

def read_stdout(stdout, queue):
    while True:
        queue.put(stdout.readline()) #This hangs when there is no IO

process = subprocess.Popen('cmd.exe', shell=False, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
q = Queue.Queue()
t = threading.Thread(target=read_stdout, args=(process.stdout, q))
t.daemon = True # t stops when the main thread stops
t.start()

while True:
    x = raw_input("insert a command \n")
    if x == "x":
        break
    process.stdin.write(x + "\n")
    o = []
    try:
        while True:
            o.append(q.get(timeout=.1))
    except Queue.Empty:
        print ''.join(o)
Bryce Guinta
  • 3,456
  • 1
  • 35
  • 36
  • Wow! this works perfectly good! I did not know about the queues in python.. now I learned and this awesome. Thank you very much. peace – Guy Jun 06 '15 at 11:52
  • @Guy You're welcome! Just know that this queue isn't just a regular queue data structure, but a 'synchronized queue' meant for multithreading. – Bryce Guinta Jun 06 '15 at 16:30
  • Not sure if it's just a Python 3.X thing, but I had to include `universal_newlines=True` as an argument to `Popen()`. Other than that this is exactly what I've been looking for for several hours now, thanks! – Reedinationer Oct 01 '19 at 17:57