0

Main script is script.py, I run two subprocesses when launching script.py as follows:

#PART ONE:
import subprocess

command = ["python", "first.py"]
command2 = ["python", "second.py"]
n = 5
for i in range(n):
    p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    p2 = subprocess.Popen(command2, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    #PART TWO:
    while True: #or while p.returncode==None:
        output = p.stdout.readline().strip()
        print output
        if output == 'stop':
            print 'success'
            p.terminate()
            p2.terminate()
            break 

So basically I'm stopping both when the subprocess p prints 'stop'. All is fine. I'm trying to make the same thing work, with the difference of launching the two subprocesses in separate terminals, by adding 'xterm','-e' to command and command2 respectively. Problem is now with p.stdout.readline().strip() I don't get access to what p prints, so I can't stop the subprocesses from the main script script.py. How should I modify my code (specially #PART TWO) so that using command = ['xterm','-e','python','first.py'] I can still read the outputs and terminate them if 'stop' is printed?

So basically #PART ONE now is:

#PART ONE:
import subprocess

command = ["xterm","-e","python", "first.py"]
command2 = ["xterm","-e","python", "second.py"]
n = 5
for i in range(n):
    p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    p2 = subprocess.Popen(command2, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

Question is how to modify #PART TWO, so that outputs can be read, and processes terminated given some condition.

  • It looks like [XY problem](http://meta.stackexchange.com/a/66378/137096)—you should try to describe the context. Consider creating GUI (e.g., using tkinter) instead of running `xterm`. To read the output from a process running in a different terminal, you could [use a named pipe](http://stackoverflow.com/a/11724655/4279). `.terminate()` might not kill the whole process tree, see [How to terminate a python subprocess launched with shell=True](http://stackoverflow.com/q/4789837) – jfs Apr 14 '16 at 21:22
  • @J.F.Sebastian haha I didnt know there was a name for it, indeed I admit this is very much an XY case :( i m reading through the links you sent, one question: but i never explicitly called shell=True when using subprocess. Popen, so does it still matter in my case whether terminate or kill is used? (Or other suggested methods in that link) –  Apr 15 '16 at 09:24
  • it doesn't matter that there is no `shell=True` in your code, as long as `Popen()` call leads to creating more than one process. The key phrase in my comment is *"kill the whole process tree"*. To understand the terms: [process group, session read this](https://www.win.tue.nl/~aeb/linux/lk/lk-10.html) – jfs Apr 15 '16 at 09:34
  • @J.F.Sebastian oh I see, makes sense. You've been very kind to me, helping out the way you have. –  Apr 15 '16 at 10:28

2 Answers2

0

One simple solution would be to redirect the stdout of first.py to a temporary file. Then modify part2 to read from that file instead of the pipe for stdout.

Alternatively, xterm man page may provide some info.

Sharad
  • 9,282
  • 3
  • 19
  • 36
  • Hi, thanks, but the issue is I don't have access to stdout of first.py anymore when using xterm –  Apr 14 '16 at 16:46
  • If you replace 'first.py' with 'first.py >> /tmp/too' and modify part2 to keep reading from /tmp/too either when you get the desired text or until a timeout duration is reached. – Sharad Apr 14 '16 at 16:49
0

I honestly do not remember what sources I put this together from but I use the following class or some modified version of this to do as you said. As far as I know the subprocess module will not allow conditional termination unless you put the process inside of a thread. The easiest way to manage the thread and process at the same time is to bundle them as a class.


import os,subprocess,threading

class RunSubprocess(object):

    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None
        self.stdOut,self.stdErr = None,None

    def run(self,timeout=100):
        def target():
            self.process = subprocess.Popen(self.cmd,
                                            shell=True,
                                            stderr=subprocess.PIPE,
                                            stdout=subprocess.PIPE,
                                            universal_newlines=True,
                                            bufsize=4096)
        self.stdOut, self.stdErr = self.process.communicate()

    self.thread = threading.Thread(target=target)
    self.thread.start()

    ## wait                            
    if timeout != None:
        self.thread.join(timeout)
        if self.thread.is_alive():
            print('The subprocess was auto-terminated due to timeout')
            print("..."+self.process.poll())
            self.process.terminate()
            self.thread.join()

        return self.process.returncode
    return None

    def terminate(self):
        if self.thread.is_alive():
            self.process.terminate()
            self.thread.join()

if __name__ == '__main__':
    cmd = "echo '...started'; sleep 2; echo '... finished'"
    myProcess = RunSubprocess(cmd)

    returnCode = myProcess.run(timeout=10)
    myProcess.terminate()
    print(myProcess.stdOut)

So I believe you could modify this class to launch two scripts and that could be closely linked.

ajrichards
  • 259
  • 2
  • 5