36

I'm trying to launch an 'rsync' using subprocess module and Popen inside of a thread. After I call the rsync I need to read the output as well. I'm using the communicate method to read the output. The code runs fine when I do not use a thread. It appears that when I use a thread it hangs on the communicate call. Another thing I've noticed is that when I set shell=False I get nothing back from the communicate when running in a thread.

noahd
  • 842
  • 2
  • 13
  • 25

2 Answers2

47

You didn't supply any code for us to look at, but here's a sample that does something similar to what you describe:

import threading
import subprocess

class MyClass(threading.Thread):
    def __init__(self):
        self.stdout = None
        self.stderr = None
        threading.Thread.__init__(self)

    def run(self):
        p = subprocess.Popen('rsync -av /etc/passwd /tmp'.split(),
                             shell=False,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)

        self.stdout, self.stderr = p.communicate()

myclass = MyClass()
myclass.start()
myclass.join()
print myclass.stdout
Ryan Bright
  • 3,495
  • 21
  • 20
  • Yes that is pretty much exactly what I am doing. I'd like to be read the output inside the thread though. I should also note I'm using Python 2.3. I've grabbed a copy of subprocess from 2.4. – noahd Jun 12 '09 at 04:55
  • I should be more clear that is what I am doing but it doesn't work. In this case the communicate call will not return anything and the command does not appear to execute. If I set shell=True the communicate hangs the thread. Then after I stop the python I end up with a defunct ssh process. – noahd Jun 12 '09 at 12:49
  • 1
    I tested the sample code provided and I get the same behavior. I tried substituting a different command like 'ls -la' and I had no issues so I think it has something to do with rsync or maybe this version of python. – noahd Jun 12 '09 at 18:20
  • 1
    Got this to work. There was something wrong with my environment. Thanks for your help! – noahd Jun 12 '09 at 18:44
  • 21
    Just curious as to what it was that you did to correct your issue? I'm in a similar position that I have a subprocess that I'm running in a thread, from which I would like to capture output as it's executing. The only time the 'communicate()' or 'stdout.readlines()' calls return any output is once the applications has completely terminated. If I pull the same code out of the thread, it works just fine. – Jason Mock Jul 11 '11 at 23:18
  • Just off the cuff, I wonder if there were buffers that weren't getting flushed quickly enough. – Jon Coombs Dec 02 '14 at 21:03
  • `'rsync -av /etc/passwd /tmp'.split()` is smart , I was `["rsync", "-av", "/etc/passwd", "/tmp"]` doing this way – alper Aug 07 '21 at 23:00
  • I have the same problem (happens on my Macbook but not Arch Linux), and get reminded of [this](https://xkcd.com/979/). – DUO Labs May 16 '22 at 23:31
  • For anyone coming for Google, my problem was that I used a `signal.pause()` right after the thread. Changing it to `threading.Event().wait()` fixed the problem. – DUO Labs May 17 '22 at 14:18
18

Here's a great implementation not using threads: constantly-print-subprocess-output-while-process-is-running

import subprocess

def execute(command):
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    output = ''

    # Poll process for new output until finished
    for line in iter(process.stdout.readline, ""):
        print line,
        output += line


    process.wait()
    exitCode = process.returncode

    if (exitCode == 0):
        return output
    else:
        raise Exception(command, exitCode, output)

execute(['ping', 'localhost'])
Community
  • 1
  • 1
nmanos
  • 181
  • 1
  • 3
  • 9
    It should be noted that this implementation will block on `process.stdout.readline()`. – Ian Oct 17 '13 at 01:58
  • Also note that adding `universal_newlines=True` to the `Popen` command is useful if the process has a loading bar, and is repeatedly printing the progress followed by `\r`. Without the `universal_newlines` option you would need to wait until the final `\n` before getting one giant string (that contains all of the progress bars concatenated together). – Aaron Voelker Mar 03 '20 at 17:59