views:

277

answers:

3

Hi,

I'm using the following to execute a process and hide its output from Python. It's in a loop though, and I need a way to block until the sub process has terminated before moving to the next iteration.

subprocess.Popen(["scanx", "--udp", host], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

Thanks for any help.

+1  A: 

Use subprocess.call(). From the docs:

subprocess.call(*popenargs, **kwargs)
Run command with arguments. Wait for command to complete, then return the returncode attribute. The arguments are the same as for the Popen constructor.

Edit:

subprocess.call() uses wait(), and wait() is vulnerable to deadlocks (as Tommy Herbert pointed out). From the docs:

Warning: This will deadlock if the child process generates enough output to a stdout or stderr pipe such that it blocks waiting for the OS pipe buffer to accept more data. Use communicate() to avoid that.

So if your command generates a lot of output, use communicate() instead:

p = subprocess.Popen(
    ["scanx", "--udp", host],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE)
out, err = p.communicate()
Ayman Hourieh
Unlike poll(), there's a danger of deadlock here.
Tommy Herbert
@Tommy, good point. Expanded on this in my answer. Thanks.
Ayman Hourieh
Thanks a lot guys :)
Jason Gooner
A: 
while not popen_obj.poll():
   pass
Tommy Herbert
The loop will needlessly waste CPU cycles.
Ayman Hourieh
A: 

If you don't need output at all you can pass devnull to stdout and stderr. I don't know if this can make a difference but pass a bufsize. Using devnull now subprocess.call doesn't suffer of deadlock anymore

import os
import subprocess

null = open(os.devnull, 'w')
subprocess.call(['ls', '-lR'], bufsize=4096, stdout=null, stderr=null)
mg