How can I run an external command asynchronously from Python?
I need to run a shell command asynchronously from a Python script. By this I mean that I want my Python script to continue running while the external command goes off and does whatever it needs to do.
I read this post:
Calling an external command in Python
I then went off and did some testing, and it looks like os.system()
will do the job provided that I use &
at the end of the command so that I don't have to wait for it to return. What I am wondering is if this is the proper way to accomplish such a thing? I tried commands.call()
but it will not work for me because it blocks on the external command.
Please let me know if using os.system()
for this is advisable or if I should try some other route.
subprocess.Popen
does exactly what you want.
from subprocess import Popen
p = Popen(['watch', 'ls']) # something long running
# ... do other stuff while subprocess is running
p.terminate()
(Edit to complete the answer from comments)
The Popen instance can do various other things like you can poll()
it to see if it is still running, and you can communicate()
with it to send it data on stdin, and wait for it to terminate.
If you want to run many processes in parallel and then handle them when they yield results, you can use polling like in the following:
from subprocess import Popen, PIPE
import time
running_procs = [
Popen(['/usr/bin/my_cmd', '-i %s' % path], stdout=PIPE, stderr=PIPE)
for path in '/tmp/file0 /tmp/file1 /tmp/file2'.split()]
while running_procs:
for proc in running_procs:
retcode = proc.poll()
if retcode is not None: # Process finished.
running_procs.remove(proc)
break
else: # No process is done, wait a bit and check again.
time.sleep(.1)
continue
# Here, `proc` has finished with return code `retcode`
if retcode != 0:
"""Error handling."""
handle_results(proc.stdout)
The control flow there is a little bit convoluted because I'm trying to make it small -- you can refactor to your taste. :-)
This has the advantage of servicing the early-finishing requests first. If you call communicate
on the first running process and that turns out to run the longest, the other running processes will have been sitting there idle when you could have been handling their results.
This is covered by Python 3 Subprocess Examples under "Wait for command to terminate asynchronously". Run this code using IPython
or python -m asyncio
:
import asyncio proc = await asyncio.create_subprocess_exec( 'ls','-lha', stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) # do something else while ls is working # if proc takes very long to complete, the CPUs are free to use cycles for # other processes stdout, stderr = await proc.communicate()
The process will start running as soon as the await asyncio.create_subprocess_exec(...)
has completed. If it hasn't finished by the time you call await proc.communicate()
, it will wait there in order to give you your output status. If it has finished, proc.communicate()
will return immediately.
The gist here is similar to Terrels answer but I think Terrels answer appears to overcomplicate things.
See asyncio.create_subprocess_exec
for more information.