Using module 'subprocess' with timeout - python
Here's the Python code to run an arbitrary command returning its stdout data, or raise an exception on non-zero exit codes:
proc = subprocess.Popen(
cmd,
stderr=subprocess.STDOUT, # Merge stdout and stderr
stdout=subprocess.PIPE,
shell=True)
communicate is used to wait for the process to exit:
stdoutdata, stderrdata = proc.communicate()
The subprocess module does not support timeout--ability to kill a process running for more than X number of seconds--therefore, communicate may take forever to run.
What is the simplest way to implement timeouts in a Python program meant to run on Windows and Linux?
In Python 3.3+:
from subprocess import STDOUT, check_output
output = check_output(cmd, stderr=STDOUT, timeout=seconds)
output is a byte string that contains command's merged stdout, stderr data.
check_output raises CalledProcessError on non-zero exit status as specified in the question's text unlike proc.communicate() method.
I've removed shell=True because it is often used unnecessarily. You can always add it back if cmd indeed requires it. If you add shell=True i.e., if the child process spawns its own descendants; check_output() can return much later than the timeout indicates, see Subprocess timeout failure.
The timeout feature is available on Python 2.x via the subprocess32 backport of the 3.2+ subprocess module.
I don't know much about the low level details; but, given that in
python 2.6 the API offers the ability to wait for threads and
terminate processes, what about running the process in a separate
thread?
import subprocess, threading
class Command(object):
def __init__(self, cmd):
self.cmd = cmd
self.process = None
def run(self, timeout):
def target():
print 'Thread started'
self.process = subprocess.Popen(self.cmd, shell=True)
self.process.communicate()
print 'Thread finished'
thread = threading.Thread(target=target)
thread.start()
thread.join(timeout)
if thread.is_alive():
print 'Terminating process'
self.process.terminate()
thread.join()
print self.process.returncode
command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)
The output of this snippet in my machine is:
Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15
where it can be seen that, in the first execution, the process
finished correctly (return code 0), while the in the second one the
process was terminated (return code -15).
I haven't tested in windows; but, aside from updating the example
command, I think it should work since I haven't found in the
documentation anything that says that thread.join or process.terminate
is not supported.
jcollado's answer can be simplified using the threading.Timer class:
import shlex
from subprocess import Popen, PIPE
from threading import Timer
def run(cmd, timeout_sec):
proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
timer = Timer(timeout_sec, proc.kill)
try:
timer.start()
stdout, stderr = proc.communicate()
finally:
timer.cancel()
# Examples: both take 1 second
run("sleep 1", 5) # process ends normally at 1 second
run("sleep 5", 1) # timeout happens at 1 second
If you're on Unix,
import signal
...
class Alarm(Exception):
pass
def alarm_handler(signum, frame):
raise Alarm
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(5*60) # 5 minutes
try:
stdoutdata, stderrdata = proc.communicate()
signal.alarm(0) # reset the alarm
except Alarm:
print "Oops, taking too long!"
# whatever else
Here is Alex Martelli's solution as a module with proper process killing. The other approaches do not work because they do not use proc.communicate(). So if you have a process that produces lots of output, it will fill its output buffer and then block until you read something from it.
from os import kill
from signal import alarm, signal, SIGALRM, SIGKILL
from subprocess import PIPE, Popen
def run(args, cwd = None, shell = False, kill_tree = True, timeout = -1, env = None):
'''
Run a command with a timeout after which it will be forcibly
killed.
'''
class Alarm(Exception):
pass
def alarm_handler(signum, frame):
raise Alarm
p = Popen(args, shell = shell, cwd = cwd, stdout = PIPE, stderr = PIPE, env = env)
if timeout != -1:
signal(SIGALRM, alarm_handler)
alarm(timeout)
try:
stdout, stderr = p.communicate()
if timeout != -1:
alarm(0)
except Alarm:
pids = [p.pid]
if kill_tree:
pids.extend(get_process_children(p.pid))
for pid in pids:
# process might have died before getting to this line
# so wrap to avoid OSError: no such process
try:
kill(pid, SIGKILL)
except OSError:
pass
return -9, '', ''
return p.returncode, stdout, stderr
def get_process_children(pid):
p = Popen('ps --no-headers -o pid --ppid %d' % pid, shell = True,
stdout = PIPE, stderr = PIPE)
stdout, stderr = p.communicate()
return [int(p) for p in stdout.split()]
if __name__ == '__main__':
print run('find /', shell = True, timeout = 3)
print run('find', shell = True)
Since Python 3.5, there's a new subprocess.run universal command (that is meant to replace check_call, check_output ...) and which has the timeout= parameter as well.
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None)
Run the command described by args. Wait for command to complete, then return a CompletedProcess instance.
It raises a subprocess.TimeoutExpired exception when the timeout expires.
timeout is now supported by call() and communicate() in the subprocess module (as of Python3.3):
import subprocess
subprocess.call("command", timeout=20, shell=True)
This will call the command and raise the exception
subprocess.TimeoutExpired
if the command doesn't finish after 20 seconds.
You can then handle the exception to continue your code, something like:
try:
subprocess.call("command", timeout=20, shell=True)
except subprocess.TimeoutExpired:
# insert code here
Hope this helps.
surprised nobody mentioned using timeout
timeout 5 ping -c 3 somehost
This won't for work for every use case obviously, but if your dealing with a simple script, this is hard to beat.
Also available as gtimeout in coreutils via homebrew for mac users.
I've modified sussudio answer. Now function returns: (returncode, stdout, stderr, timeout) - stdout and stderr is decoded to utf-8 string
def kill_proc(proc, timeout):
timeout["value"] = True
proc.kill()
def run(cmd, timeout_sec):
proc = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
timeout = {"value": False}
timer = Timer(timeout_sec, kill_proc, [proc, timeout])
timer.start()
stdout, stderr = proc.communicate()
timer.cancel()
return proc.returncode, stdout.decode("utf-8"), stderr.decode("utf-8"), timeout["value"]
Another option is to write to a temporary file to prevent the stdout blocking instead of needing to poll with communicate(). This worked for me where the other answers did not; for example on windows.
outFile = tempfile.SpooledTemporaryFile()
errFile = tempfile.SpooledTemporaryFile()
proc = subprocess.Popen(args, stderr=errFile, stdout=outFile, universal_newlines=False)
wait_remaining_sec = timeout
while proc.poll() is None and wait_remaining_sec > 0:
time.sleep(1)
wait_remaining_sec -= 1
if wait_remaining_sec <= 0:
killProc(proc.pid)
raise ProcessIncompleteError(proc, timeout)
# read temp streams from start
outFile.seek(0);
errFile.seek(0);
out = outFile.read()
err = errFile.read()
outFile.close()
errFile.close()
Prepending the Linux command timeout isn't a bad workaround and it worked for me.
cmd = "timeout 20 "+ cmd
subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(output, err) = p.communicate()
I added the solution with threading from jcollado to my Python module easyprocess.
Install:
pip install easyprocess
Example:
from easyprocess import Proc
# shell is not supported!
stdout=Proc('ping localhost').call(timeout=1.5).stdout
print stdout
Here is my solution, I was using Thread and Event:
import subprocess
from threading import Thread, Event
def kill_on_timeout(done, timeout, proc):
if not done.wait(timeout):
proc.kill()
def exec_command(command, timeout):
done = Event()
proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
watcher = Thread(target=kill_on_timeout, args=(done, timeout, proc))
watcher.daemon = True
watcher.start()
data, stderr = proc.communicate()
done.set()
return data, stderr, proc.returncode
In action:
In [2]: exec_command(['sleep', '10'], 5)
Out[2]: ('', '', -9)
In [3]: exec_command(['sleep', '10'], 11)
Out[3]: ('', '', 0)
The solution I use is to prefix the shell command with timelimit. If the comand takes too long, timelimit will stop it and Popen will have a returncode set by timelimit. If it is > 128, it means timelimit killed the process.
See also python subprocess with timeout and large output (>64K)
if you are using python 2, give it a try
import subprocess32
try:
output = subprocess32.check_output(command, shell=True, timeout=3)
except subprocess32.TimeoutExpired as e:
print e
I've implemented what I could gather from a few of these. This works in Windows, and since this is a community wiki, I figure I would share my code as well:
class Command(threading.Thread):
def __init__(self, cmd, outFile, errFile, timeout):
threading.Thread.__init__(self)
self.cmd = cmd
self.process = None
self.outFile = outFile
self.errFile = errFile
self.timed_out = False
self.timeout = timeout
def run(self):
self.process = subprocess.Popen(self.cmd, stdout = self.outFile, \
stderr = self.errFile)
while (self.process.poll() is None and self.timeout > 0):
time.sleep(1)
self.timeout -= 1
if not self.timeout > 0:
self.process.terminate()
self.timed_out = True
else:
self.timed_out = False
Then from another class or file:
outFile = tempfile.SpooledTemporaryFile()
errFile = tempfile.SpooledTemporaryFile()
executor = command.Command(c, outFile, errFile, timeout)
executor.daemon = True
executor.start()
executor.join()
if executor.timed_out:
out = 'timed out'
else:
outFile.seek(0)
errFile.seek(0)
out = outFile.read()
err = errFile.read()
outFile.close()
errFile.close()
Once you understand full process running machinery in *unix, you will easily find simplier solution:
Consider this simple example how to make timeoutable communicate() meth using select.select() (available alsmost everythere on *nix nowadays). This also can be written with epoll/poll/kqueue, but select.select() variant could be a good example for you. And major limitations of select.select() (speed and 1024 max fds) are not applicapable for your task.
This works under *nix, does not create threads, does not uses signals, can be lauched from any thread (not only main), and fast enought to read 250mb/s of data from stdout on my machine (i5 2.3ghz).
There is a problem in join'ing stdout/stderr at the end of communicate. If you have huge program output this could lead to big memory usage. But you can call communicate() several times with smaller timeouts.
class Popen(subprocess.Popen):
def communicate(self, input=None, timeout=None):
if timeout is None:
return subprocess.Popen.communicate(self, input)
if self.stdin:
# Flush stdio buffer, this might block if user
# has been writing to .stdin in an uncontrolled
# fashion.
self.stdin.flush()
if not input:
self.stdin.close()
read_set, write_set = [], []
stdout = stderr = None
if self.stdin and input:
write_set.append(self.stdin)
if self.stdout:
read_set.append(self.stdout)
stdout = []
if self.stderr:
read_set.append(self.stderr)
stderr = []
input_offset = 0
deadline = time.time() + timeout
while read_set or write_set:
try:
rlist, wlist, xlist = select.select(read_set, write_set, [], max(0, deadline - time.time()))
except select.error as ex:
if ex.args[0] == errno.EINTR:
continue
raise
if not (rlist or wlist):
# Just break if timeout
# Since we do not close stdout/stderr/stdin, we can call
# communicate() several times reading data by smaller pieces.
break
if self.stdin in wlist:
chunk = input[input_offset:input_offset + subprocess._PIPE_BUF]
try:
bytes_written = os.write(self.stdin.fileno(), chunk)
except OSError as ex:
if ex.errno == errno.EPIPE:
self.stdin.close()
write_set.remove(self.stdin)
else:
raise
else:
input_offset += bytes_written
if input_offset >= len(input):
self.stdin.close()
write_set.remove(self.stdin)
# Read stdout / stderr by 1024 bytes
for fn, tgt in (
(self.stdout, stdout),
(self.stderr, stderr),
):
if fn in rlist:
data = os.read(fn.fileno(), 1024)
if data == '':
fn.close()
read_set.remove(fn)
tgt.append(data)
if stdout is not None:
stdout = ''.join(stdout)
if stderr is not None:
stderr = ''.join(stderr)
return (stdout, stderr)
You can do this using select
import subprocess
from datetime import datetime
from select import select
def call_with_timeout(cmd, timeout):
started = datetime.now()
sp = subprocess.Popen(cmd, stdout=subprocess.PIPE)
while True:
p = select([sp.stdout], [], [], timeout)
if p[0]:
p[0][0].read()
ret = sp.poll()
if ret is not None:
return ret
if (datetime.now()-started).total_seconds() > timeout:
sp.kill()
return None
python 2.7
import time
import subprocess
def run_command(cmd, timeout=0):
start_time = time.time()
df = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while timeout and df.poll() == None:
if time.time()-start_time >= timeout:
df.kill()
return -1, ""
output = '\n'.join(df.communicate()).strip()
return df.returncode, output
Example of captured output after timeout tested in Python 3.7.8:
try:
return subprocess.run(command, shell=True, capture_output=True, timeout=20, cwd=cwd, universal_newlines=True)
except subprocess.TimeoutExpired as e:
print(e.output.decode(encoding="utf-8", errors="ignore"))
assert False;
The exception subprocess.TimeoutExpired has the output and other members:
cmd - Command that was used to spawn the child process.
timeout - Timeout in seconds.
output - Output of the child process if it was captured by run() or
check_output(). Otherwise, None.
stdout - Alias for output, for symmetry with stderr.
stderr - Stderr output of the child process if it was captured by
run(). Otherwise, None.
More info: https://docs.python.org/3/library/subprocess.html#subprocess.TimeoutExpired
I've used killableprocess successfully on Windows, Linux and Mac. If you are using Cygwin Python, you'll need OSAF's version of killableprocess because otherwise native Windows processes won't get killed.
Although I haven't looked at it extensively, this decorator I found at ActiveState seems to be quite useful for this sort of thing. Along with subprocess.Popen(..., close_fds=True), at least I'm ready for shell-scripting in Python.
This solution kills the process tree in case of shell=True, passes parameters to the process (or not), has a timeout and gets the stdout, stderr and process output of the call back (it uses psutil for the kill_proc_tree). This was based on several solutions posted in SO including jcollado's. Posting in response to comments by Anson and jradice in jcollado's answer. Tested in Windows Srvr 2012 and Ubuntu 14.04. Please note that for Ubuntu you need to change the parent.children(...) call to parent.get_children(...).
def kill_proc_tree(pid, including_parent=True):
parent = psutil.Process(pid)
children = parent.children(recursive=True)
for child in children:
child.kill()
psutil.wait_procs(children, timeout=5)
if including_parent:
parent.kill()
parent.wait(5)
def run_with_timeout(cmd, current_dir, cmd_parms, timeout):
def target():
process = subprocess.Popen(cmd, cwd=current_dir, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
# wait for the process to terminate
if (cmd_parms == ""):
out, err = process.communicate()
else:
out, err = process.communicate(cmd_parms)
errcode = process.returncode
thread = Thread(target=target)
thread.start()
thread.join(timeout)
if thread.is_alive():
me = os.getpid()
kill_proc_tree(me, including_parent=False)
thread.join()
There's an idea to subclass the Popen class and extend it with some simple method decorators. Let's call it ExpirablePopen.
from logging import error
from subprocess import Popen
from threading import Event
from threading import Thread
class ExpirablePopen(Popen):
def __init__(self, *args, **kwargs):
self.timeout = kwargs.pop('timeout', 0)
self.timer = None
self.done = Event()
Popen.__init__(self, *args, **kwargs)
def __tkill(self):
timeout = self.timeout
if not self.done.wait(timeout):
error('Terminating process {} by timeout of {} secs.'.format(self.pid, timeout))
self.kill()
def expirable(func):
def wrapper(self, *args, **kwargs):
# zero timeout means call of parent method
if self.timeout == 0:
return func(self, *args, **kwargs)
# if timer is None, need to start it
if self.timer is None:
self.timer = thr = Thread(target=self.__tkill)
thr.daemon = True
thr.start()
result = func(self, *args, **kwargs)
self.done.set()
return result
return wrapper
wait = expirable(Popen.wait)
communicate = expirable(Popen.communicate)
if __name__ == '__main__':
from subprocess import PIPE
print ExpirablePopen('ssh -T git#bitbucket.org', stdout=PIPE, timeout=1).communicate()
I had the problem that I wanted to terminate a multithreading subprocess if it took longer than a given timeout length. I wanted to set a timeout in Popen(), but it did not work. Then, I realized that Popen().wait() is equal to call() and so I had the idea to set a timeout within the .wait(timeout=xxx) method, which finally worked. Thus, I solved it this way:
import os
import sys
import signal
import subprocess
from multiprocessing import Pool
cores_for_parallelization = 4
timeout_time = 15 # seconds
def main():
jobs = [...YOUR_JOB_LIST...]
with Pool(cores_for_parallelization) as p:
p.map(run_parallel_jobs, jobs)
def run_parallel_jobs(args):
# Define the arguments including the paths
initial_terminal_command = 'C:\\Python34\\python.exe' # Python executable
function_to_start = 'C:\\temp\\xyz.py' # The multithreading script
final_list = [initial_terminal_command, function_to_start]
final_list.extend(args)
# Start the subprocess and determine the process PID
subp = subprocess.Popen(final_list) # starts the process
pid = subp.pid
# Wait until the return code returns from the function by considering the timeout.
# If not, terminate the process.
try:
returncode = subp.wait(timeout=timeout_time) # should be zero if accomplished
except subprocess.TimeoutExpired:
# Distinguish between Linux and Windows and terminate the process if
# the timeout has been expired
if sys.platform == 'linux2':
os.kill(pid, signal.SIGTERM)
elif sys.platform == 'win32':
subp.terminate()
if __name__ == '__main__':
main()
Late answer for Linux only, but in case someone wants to use subprocess.getstatusoutput(), where the timeout argument isn't available, you can use the built-in Linux timeout on the beginning of the command, i.e.:
import subprocess
timeout = 25 # seconds
cmd = f"timeout --preserve-status --foreground {timeout} ping duckgo.com"
exit_c, out = subprocess.getstatusoutput(cmd)
if (exit_c == 0):
print("success")
else:
print("Error: ", out)
timeout Arguments:
--preserve-status : Preserving the Exit Status
--foreground : Running in Foreground
25 : timeout value in seconds
Unfortunately, I'm bound by very strict policies on the disclosure of source code by my employer, so I can't provide actual code. But for my taste the best solution is to create a subclass overriding Popen.wait() to poll instead of wait indefinitely, and Popen.__init__ to accept a timeout parameter. Once you do that, all the other Popen methods (which call wait) will work as expected, including communicate.
https://pypi.python.org/pypi/python-subprocess2 provides extensions to the subprocess module which allow you to wait up to a certain period of time, otherwise terminate.
So, to wait up to 10 seconds for the process to terminate, otherwise kill:
pipe = subprocess.Popen('...')
timeout = 10
results = pipe.waitOrTerminate(timeout)
This is compatible with both windows and unix. "results" is a dictionary, it contains "returnCode" which is the return of the app (or None if it had to be killed), as well as "actionTaken". which will be "SUBPROCESS2_PROCESS_COMPLETED" if the process completed normally, or a mask of "SUBPROCESS2_PROCESS_TERMINATED" and SUBPROCESS2_PROCESS_KILLED depending on action taken (see documentation for full details)
for python 2.6+, use gevent
from gevent.subprocess import Popen, PIPE, STDOUT
def call_sys(cmd, timeout):
p= Popen(cmd, shell=True, stdout=PIPE)
output, _ = p.communicate(timeout=timeout)
assert p.returncode == 0, p. returncode
return output
call_sys('./t.sh', 2)
# t.sh example
sleep 5
echo done
exit 1
Sometimes you need to process (ffmpeg) without using communicate() and in this case you need asynchronous timeout, a practical way to do this using ttldict
pip install ttldict
from ttldict import TTLOrderedDict
sp_timeout = TTLOrderedDict(default_ttl=10)
def kill_on_timeout(done, proc):
while True:
now = time.time()
if sp_timeout.get('exp_time') == None:
proc.kill()
break
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True, stderr=subprocess.STDOUT)
sp_timeout['exp_time'] = time.time()
done = Event()
watcher = Thread(target=kill_on_timeout, args=(done, process))
watcher.daemon = True
watcher.start()
done.set()
for line in process.stdout:
.......
Related
Python subprocess: How can I skip the process.stdout.readline() request if there is no new line? [duplicate]
I'm using the subprocess module to start a subprocess and connect to its output stream (standard output). I want to be able to execute non-blocking reads on its standard output. Is there a way to make .readline non-blocking or to check if there is data on the stream before I invoke .readline? I'd like this to be portable or at least work under Windows and Linux. Here is how I do it for now (it's blocking on the .readline if no data is available): p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE) output_str = p.stdout.readline()
fcntl, select, asyncproc won't help in this case. A reliable way to read a stream without blocking regardless of operating system is to use Queue.get_nowait(): import sys from subprocess import PIPE, Popen from threading import Thread try: from queue import Queue, Empty except ImportError: from Queue import Queue, Empty # python 2.x ON_POSIX = 'posix' in sys.builtin_module_names def enqueue_output(out, queue): for line in iter(out.readline, b''): queue.put(line) out.close() p = Popen(['myprogram.exe'], stdout=PIPE, bufsize=1, close_fds=ON_POSIX) q = Queue() t = Thread(target=enqueue_output, args=(p.stdout, q)) t.daemon = True # thread dies with the program t.start() # ... do other things here # read line without blocking try: line = q.get_nowait() # or q.get(timeout=.1) except Empty: print('no output yet') else: # got line # ... do something with line
I have often had a similar problem; Python programs I write frequently need to have the ability to execute some primary functionality while simultaneously accepting user input from the command line (stdin). Simply putting the user input handling functionality in another thread doesn't solve the problem because readline() blocks and has no timeout. If the primary functionality is complete and there is no longer any need to wait for further user input I typically want my program to exit, but it can't because readline() is still blocking in the other thread waiting for a line. A solution I have found to this problem is to make stdin a non-blocking file using the fcntl module: import fcntl import os import sys # make stdin a non-blocking file fd = sys.stdin.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) # user input handling thread while mainThreadIsRunning: try: input = sys.stdin.readline() except: continue handleInput(input) In my opinion this is a bit cleaner than using the select or signal modules to solve this problem but then again it only works on UNIX...
Python 3.4 introduces new provisional API for asynchronous IO -- asyncio module. The approach is similar to twisted-based answer by #Bryan Ward -- define a protocol and its methods are called as soon as data is ready: #!/usr/bin/env python3 import asyncio import os class SubprocessProtocol(asyncio.SubprocessProtocol): def pipe_data_received(self, fd, data): if fd == 1: # got stdout data (bytes) print(data) def connection_lost(self, exc): loop.stop() # end loop.run_forever() if os.name == 'nt': loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows asyncio.set_event_loop(loop) else: loop = asyncio.get_event_loop() try: loop.run_until_complete(loop.subprocess_exec(SubprocessProtocol, "myprogram.exe", "arg1", "arg2")) loop.run_forever() finally: loop.close() See "Subprocess" in the docs. There is a high-level interface asyncio.create_subprocess_exec() that returns Process objects that allows to read a line asynchroniosly using StreamReader.readline() coroutine (with async/await Python 3.5+ syntax): #!/usr/bin/env python3.5 import asyncio import locale import sys from asyncio.subprocess import PIPE from contextlib import closing async def readline_and_kill(*args): # start child process process = await asyncio.create_subprocess_exec(*args, stdout=PIPE) # read line (sequence of bytes ending with b'\n') asynchronously async for line in process.stdout: print("got line:", line.decode(locale.getpreferredencoding(False))) break process.kill() return await process.wait() # wait for the child process to exit if sys.platform == "win32": loop = asyncio.ProactorEventLoop() asyncio.set_event_loop(loop) else: loop = asyncio.get_event_loop() with closing(loop): sys.exit(loop.run_until_complete(readline_and_kill( "myprogram.exe", "arg1", "arg2"))) readline_and_kill() performs the following tasks: start subprocess, redirect its stdout to a pipe read a line from subprocess' stdout asynchronously kill subprocess wait for it to exit Each step could be limited by timeout seconds if necessary.
On Unix-like systems and Python 3.5+ there's os.set_blocking which does exactly what it says. import os import time import subprocess cmd = 'python3', '-c', 'import time; [(print(i), time.sleep(1)) for i in range(5)]' p = subprocess.Popen(cmd, stdout=subprocess.PIPE) os.set_blocking(p.stdout.fileno(), False) start = time.time() while True: # first iteration always produces empty byte string in non-blocking mode for i in range(2): line = p.stdout.readline() print(i, line) time.sleep(0.5) if time.time() > start + 5: break p.terminate() This outputs: 1 b'' 2 b'0\n' 1 b'' 2 b'1\n' 1 b'' 2 b'2\n' 1 b'' 2 b'3\n' 1 b'' 2 b'4\n' With os.set_blocking commented it's: 0 b'0\n' 1 b'1\n' 0 b'2\n' 1 b'3\n' 0 b'4\n' 1 b''
Try the asyncproc module. For example: import os from asyncproc import Process myProc = Process("myprogram.app") while True: # check to see if process has ended poll = myProc.wait(os.WNOHANG) if poll != None: break # print any new output out = myProc.read() if out != "": print out The module takes care of all the threading as suggested by S.Lott.
You can do this really easily in Twisted. Depending upon your existing code base, this might not be that easy to use, but if you are building a twisted application, then things like this become almost trivial. You create a ProcessProtocol class, and override the outReceived() method. Twisted (depending upon the reactor used) is usually just a big select() loop with callbacks installed to handle data from different file descriptors (often network sockets). So the outReceived() method is simply installing a callback for handling data coming from STDOUT. A simple example demonstrating this behavior is as follows: from twisted.internet import protocol, reactor class MyProcessProtocol(protocol.ProcessProtocol): def outReceived(self, data): print data proc = MyProcessProtocol() reactor.spawnProcess(proc, './myprogram', ['./myprogram', 'arg1', 'arg2', 'arg3']) reactor.run() The Twisted documentation has some good information on this. If you build your entire application around Twisted, it makes asynchronous communication with other processes, local or remote, really elegant like this. On the other hand, if your program isn't built on top of Twisted, this isn't really going to be that helpful. Hopefully this can be helpful to other readers, even if it isn't applicable for your particular application.
Use select & read(1). import subprocess #no new requirements def readAllSoFar(proc, retVal=''): while (select.select([proc.stdout],[],[],0)[0]!=[]): retVal+=proc.stdout.read(1) return retVal p = subprocess.Popen(['/bin/ls'], stdout=subprocess.PIPE) while not p.poll(): print (readAllSoFar(p)) For readline()-like: lines = [''] while not p.poll(): lines = readAllSoFar(p, lines[-1]).split('\n') for a in range(len(lines)-1): print a lines = readAllSoFar(p, lines[-1]).split('\n') for a in range(len(lines)-1): print a
Things are a lot better in modern Python. Here's a simple child program, "hello.py": #!/usr/bin/env python3 while True: i = input() if i == "quit": break print(f"hello {i}") And a program to interact with it: import asyncio async def main(): proc = await asyncio.subprocess.create_subprocess_exec( "./hello.py", stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE ) proc.stdin.write(b"bob\n") print(await proc.stdout.read(1024)) proc.stdin.write(b"alice\n") print(await proc.stdout.read(1024)) proc.stdin.write(b"quit\n") await proc.wait() asyncio.run(main()) That prints out: b'hello bob\n' b'hello alice\n' Note that the actual pattern, which is also by almost all of the previous answers, both here and in related questions, is to set the child's stdout file descriptor to non-blocking and then poll it in some sort of select loop. These days, of course, that loop is provided by asyncio.
One solution is to make another process to perform your read of the process, or make a thread of the process with a timeout. Here's the threaded version of a timeout function: http://code.activestate.com/recipes/473878/ However, do you need to read the stdout as it's coming in? Another solution may be to dump the output to a file and wait for the process to finish using p.wait(). f = open('myprogram_output.txt','w') p = subprocess.Popen('myprogram.exe', stdout=f) p.wait() f.close() str = open('myprogram_output.txt','r').read()
Here is my code, used to catch every output from subprocess ASAP, including partial lines. It pumps at same time and stdout and stderr in almost correct order. Tested and correctly worked on Python 2.7 linux & windows. #!/usr/bin/python # # Runner with stdout/stderr catcher # from sys import argv from subprocess import Popen, PIPE import os, io from threading import Thread import Queue def __main__(): if (len(argv) > 1) and (argv[-1] == "-sub-"): import time, sys print "Application runned!" time.sleep(2) print "Slept 2 second" time.sleep(1) print "Slept 1 additional second", time.sleep(2) sys.stderr.write("Stderr output after 5 seconds") print "Eol on stdin" sys.stderr.write("Eol on stderr\n") time.sleep(1) print "Wow, we have end of work!", else: os.environ["PYTHONUNBUFFERED"]="1" try: p = Popen( argv + ["-sub-"], bufsize=0, # line-buffered stdin=PIPE, stdout=PIPE, stderr=PIPE ) except WindowsError, W: if W.winerror==193: p = Popen( argv + ["-sub-"], shell=True, # Try to run via shell bufsize=0, # line-buffered stdin=PIPE, stdout=PIPE, stderr=PIPE ) else: raise inp = Queue.Queue() sout = io.open(p.stdout.fileno(), 'rb', closefd=False) serr = io.open(p.stderr.fileno(), 'rb', closefd=False) def Pump(stream, category): queue = Queue.Queue() def rdr(): while True: buf = stream.read1(8192) if len(buf)>0: queue.put( buf ) else: queue.put( None ) return def clct(): active = True while active: r = queue.get() try: while True: r1 = queue.get(timeout=0.005) if r1 is None: active = False break else: r += r1 except Queue.Empty: pass inp.put( (category, r) ) for tgt in [rdr, clct]: th = Thread(target=tgt) th.setDaemon(True) th.start() Pump(sout, 'stdout') Pump(serr, 'stderr') while p.poll() is None: # App still working try: chan,line = inp.get(timeout = 1.0) if chan=='stdout': print "STDOUT>>", line, "<?<" elif chan=='stderr': print " ERROR==", line, "=?=" except Queue.Empty: pass print "Finish" if __name__ == '__main__': __main__()
Disclaimer: this works only for tornado You can do this by setting the fd to be nonblocking and then use ioloop to register callbacks. I have packaged this in an egg called tornado_subprocess and you can install it via PyPI: easy_install tornado_subprocess now you can do something like this: import tornado_subprocess import tornado.ioloop def print_res( status, stdout, stderr ) : print status, stdout, stderr if status == 0: print "OK:" print stdout else: print "ERROR:" print stderr t = tornado_subprocess.Subprocess( print_res, timeout=30, args=[ "cat", "/etc/passwd" ] ) t.start() tornado.ioloop.IOLoop.instance().start() you can also use it with a RequestHandler class MyHandler(tornado.web.RequestHandler): def on_done(self, status, stdout, stderr): self.write( stdout ) self.finish() #tornado.web.asynchronous def get(self): t = tornado_subprocess.Subprocess( self.on_done, timeout=30, args=[ "cat", "/etc/passwd" ] ) t.start()
Existing solutions did not work for me (details below). What finally worked was to implement readline using read(1) (based on this answer). The latter does not block: from subprocess import Popen, PIPE from threading import Thread def process_output(myprocess): #output-consuming thread nextline = None buf = '' while True: #--- extract line using read(1) out = myprocess.stdout.read(1) if out == '' and myprocess.poll() != None: break if out != '': buf += out if out == '\n': nextline = buf buf = '' if not nextline: continue line = nextline nextline = None #--- do whatever you want with line here print 'Line is:', line myprocess.stdout.close() myprocess = Popen('myprogram.exe', stdout=PIPE) #output-producing process p1 = Thread(target=process_output, args=(myprocess,)) #output-consuming thread p1.daemon = True p1.start() #--- do whatever here and then kill process and thread if needed if myprocess.poll() == None: #kill process; will automatically stop thread myprocess.kill() myprocess.wait() if p1 and p1.is_alive(): #wait for thread to finish p1.join() Why existing solutions did not work: Solutions that require readline (including the Queue based ones) always block. It is difficult (impossible?) to kill the thread that executes readline. It only gets killed when the process that created it finishes, but not when the output-producing process is killed. Mixing low-level fcntl with high-level readline calls may not work properly as anonnn has pointed out. Using select.poll() is neat, but doesn't work on Windows according to python docs. Using third-party libraries seems overkill for this task and adds additional dependencies.
I add this problem to read some subprocess.Popen stdout. Here is my non blocking read solution: import fcntl def non_block_read(output): fd = output.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) try: return output.read() except: return "" # Use example from subprocess import * sb = Popen("echo test && sleep 1000", shell=True, stdout=PIPE) sb.kill() # sb.stdout.read() # <-- This will block non_block_read(sb.stdout) 'test\n'
Here is a simple solution based on threads which: works on both Linux and Windows (not relying on select). reads both stdout and stderr asynchronouly. doesn't rely on active polling with arbitrary waiting time (CPU friendly). doesn't use asyncio (which may conflict with other libraries). runs until the child process terminates. printer.py import time import sys sys.stdout.write("Hello\n") sys.stdout.flush() time.sleep(1) sys.stdout.write("World!\n") sys.stdout.flush() time.sleep(1) sys.stderr.write("That's an error\n") sys.stderr.flush() time.sleep(2) sys.stdout.write("Actually, I'm fine\n") sys.stdout.flush() time.sleep(1) reader.py import queue import subprocess import sys import threading def enqueue_stream(stream, queue, type): for line in iter(stream.readline, b''): queue.put(str(type) + line.decode('utf-8')) stream.close() def enqueue_process(process, queue): process.wait() queue.put('x') p = subprocess.Popen('python printer.py', stdout=subprocess.PIPE, stderr=subprocess.PIPE) q = queue.Queue() to = threading.Thread(target=enqueue_stream, args=(p.stdout, q, 1)) te = threading.Thread(target=enqueue_stream, args=(p.stderr, q, 2)) tp = threading.Thread(target=enqueue_process, args=(p, q)) te.start() to.start() tp.start() while True: line = q.get() if line[0] == 'x': break if line[0] == '2': # stderr sys.stdout.write("\033[0;31m") # ANSI red color sys.stdout.write(line[1:]) if line[0] == '2': sys.stdout.write("\033[0m") # reset ANSI code sys.stdout.flush() tp.join() to.join() te.join()
This version of non-blocking read doesn't require special modules and will work out-of-the-box on majority of Linux distros. import os import sys import time import fcntl import subprocess def async_read(fd): # set non-blocking flag while preserving old flags fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) # read char until EOF hit while True: try: ch = os.read(fd.fileno(), 1) # EOF if not ch: break sys.stdout.write(ch) except OSError: # waiting for data be available on fd pass def shell(args, async=True): # merge stderr and stdout proc = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if async: async_read(proc.stdout) sout, serr = proc.communicate() return (sout, serr) if __name__ == '__main__': cmd = 'ping 8.8.8.8' sout, serr = shell(cmd.split())
I have the original questioner's problem, but did not wish to invoke threads. I mixed Jesse's solution with a direct read() from the pipe, and my own buffer-handler for line reads (however, my sub-process - ping - always wrote full lines < a system page size). I avoid busy-waiting by only reading in a gobject-registered io watch. These days I usually run code within a gobject MainLoop to avoid threads. def set_up_ping(ip, w): # run the sub-process # watch the resultant pipe p = subprocess.Popen(['/bin/ping', ip], stdout=subprocess.PIPE) # make stdout a non-blocking file fl = fcntl.fcntl(p.stdout, fcntl.F_GETFL) fcntl.fcntl(p.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK) stdout_gid = gobject.io_add_watch(p.stdout, gobject.IO_IN, w) return stdout_gid # for shutting down The watcher is def watch(f, *other): print 'reading',f.read() return True And the main program sets up a ping and then calls gobject mail loop. def main(): set_up_ping('192.168.1.8', watch) # discard gid as unused here gobject.MainLoop().run() Any other work is attached to callbacks in gobject.
Adding this answer here since it provides ability to set non-blocking pipes on Windows and Unix. All the ctypes details are thanks to #techtonik's answer. There is a slightly modified version to be used both on Unix and Windows systems. Python3 compatible (only minor change needed). Includes posix version, and defines exception to use for either. This way you can use the same function and exception for Unix and Windows code. # pipe_non_blocking.py (module) """ Example use: p = subprocess.Popen( command, stdout=subprocess.PIPE, ) pipe_non_blocking_set(p.stdout.fileno()) try: data = os.read(p.stdout.fileno(), 1) except PortableBlockingIOError as ex: if not pipe_non_blocking_is_error_blocking(ex): raise ex """ __all__ = ( "pipe_non_blocking_set", "pipe_non_blocking_is_error_blocking", "PortableBlockingIOError", ) import os if os.name == "nt": def pipe_non_blocking_set(fd): # Constant could define globally but avoid polluting the name-space # thanks to: https://stackoverflow.com/questions/34504970 import msvcrt from ctypes import windll, byref, wintypes, WinError, POINTER from ctypes.wintypes import HANDLE, DWORD, BOOL LPDWORD = POINTER(DWORD) PIPE_NOWAIT = wintypes.DWORD(0x00000001) def pipe_no_wait(pipefd): SetNamedPipeHandleState = windll.kernel32.SetNamedPipeHandleState SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD] SetNamedPipeHandleState.restype = BOOL h = msvcrt.get_osfhandle(pipefd) res = windll.kernel32.SetNamedPipeHandleState(h, byref(PIPE_NOWAIT), None, None) if res == 0: print(WinError()) return False return True return pipe_no_wait(fd) def pipe_non_blocking_is_error_blocking(ex): if not isinstance(ex, PortableBlockingIOError): return False from ctypes import GetLastError ERROR_NO_DATA = 232 return (GetLastError() == ERROR_NO_DATA) PortableBlockingIOError = OSError else: def pipe_non_blocking_set(fd): import fcntl fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) return True def pipe_non_blocking_is_error_blocking(ex): if not isinstance(ex, PortableBlockingIOError): return False return True PortableBlockingIOError = BlockingIOError To avoid reading incomplete data, I ended up writing my own readline generator (which returns the byte string for each line). Its a generator so you can for example... def non_blocking_readlines(f, chunk=1024): """ Iterate over lines, yielding b'' when nothings left or when new data is not yet available. stdout_iter = iter(non_blocking_readlines(process.stdout)) line = next(stdout_iter) # will be a line or b''. """ import os from .pipe_non_blocking import ( pipe_non_blocking_set, pipe_non_blocking_is_error_blocking, PortableBlockingIOError, ) fd = f.fileno() pipe_non_blocking_set(fd) blocks = [] while True: try: data = os.read(fd, chunk) if not data: # case were reading finishes with no trailing newline yield b''.join(blocks) blocks.clear() except PortableBlockingIOError as ex: if not pipe_non_blocking_is_error_blocking(ex): raise ex yield b'' continue while True: n = data.find(b'\n') if n == -1: break yield b''.join(blocks) + data[:n + 1] data = data[n + 1:] blocks.clear() blocks.append(data)
Not the first and probably not the last, I have built a package that does non blocking stdout PIPE reads with two different methods, one being based on the work of J.F. Sebastian (#jfs)'s answer, the other being a simple communicate() loop with a thread to check for timeouts. Both stdout capture methods are tested to work both under Linux and Windows, with Python versions from 2.7 to 3.9 as of the time of writing Being non blocking, it guarantees timeout enforcement, even with multiple child and grandchild processes, and even under Python 2.7. The package also handles both bytes and text stdout encodings, being a nightmare when trying to catch EOF. You'll find the package at https://github.com/netinvent/command_runner If you need some well tested non blocking read implementations, try it out (or hack the code): pip install command_runner from command_runner import command_runner exit_code, output = command_runner('ping 127.0.0.1', timeout=3) exit_code, output = command_runner('echo hello world, shell=True) exit_code, output = command_runner('some command', stdout='some_file') You can find the core non blocking read code in _poll_process() or _monitor_process() depending on the capture method employed. From there, you can hack your way to what you want, or simply use the whole package to execute your commands as a subprocess replacement.
The select module helps you determine where the next useful input is. However, you're almost always happier with separate threads. One does a blocking read the stdin, another does wherever it is you don't want blocked.
why bothering thread&queue? unlike readline(), BufferedReader.read1() wont block waiting for \r\n, it returns ASAP if there is any output coming in. #!/usr/bin/python from subprocess import Popen, PIPE, STDOUT import io def __main__(): try: p = Popen( ["ping", "-n", "3", "127.0.0.1"], stdin=PIPE, stdout=PIPE, stderr=STDOUT ) except: print("Popen failed"); quit() sout = io.open(p.stdout.fileno(), 'rb', closefd=False) while True: buf = sout.read1(1024) if len(buf) == 0: break print buf, if __name__ == '__main__': __main__()
In my case I needed a logging module that catches the output from the background applications and augments it(adding time-stamps, colors, etc.). I ended up with a background thread that does the actual I/O. Following code is only for POSIX platforms. I stripped non-essential parts. If someone is going to use this beast for long runs consider managing open descriptors. In my case it was not a big problem. # -*- python -*- import fcntl import threading import sys, os, errno import subprocess class Logger(threading.Thread): def __init__(self, *modules): threading.Thread.__init__(self) try: from select import epoll, EPOLLIN self.__poll = epoll() self.__evt = EPOLLIN self.__to = -1 except: from select import poll, POLLIN print 'epoll is not available' self.__poll = poll() self.__evt = POLLIN self.__to = 100 self.__fds = {} self.daemon = True self.start() def run(self): while True: events = self.__poll.poll(self.__to) for fd, ev in events: if (ev&self.__evt) != self.__evt: continue try: self.__fds[fd].run() except Exception, e: print e def add(self, fd, log): assert not self.__fds.has_key(fd) self.__fds[fd] = log self.__poll.register(fd, self.__evt) class log: logger = Logger() def __init__(self, name): self.__name = name self.__piped = False def fileno(self): if self.__piped: return self.write self.read, self.write = os.pipe() fl = fcntl.fcntl(self.read, fcntl.F_GETFL) fcntl.fcntl(self.read, fcntl.F_SETFL, fl | os.O_NONBLOCK) self.fdRead = os.fdopen(self.read) self.logger.add(self.read, self) self.__piped = True return self.write def __run(self, line): self.chat(line, nl=False) def run(self): while True: try: line = self.fdRead.readline() except IOError, exc: if exc.errno == errno.EAGAIN: return raise self.__run(line) def chat(self, line, nl=True): if nl: nl = '\n' else: nl = '' sys.stdout.write('[%s] %s%s' % (self.__name, line, nl)) def system(command, param=[], cwd=None, env=None, input=None, output=None): args = [command] + param p = subprocess.Popen(args, cwd=cwd, stdout=output, stderr=output, stdin=input, env=env, bufsize=0) p.wait() ls = log('ls') ls.chat('go') system("ls", ['-l', '/'], output=ls) date = log('date') date.chat('go') system("date", output=date)
This is a example to run interactive command in subprocess, and the stdout is interactive by using pseudo terminal. You can refer to: https://stackoverflow.com/a/43012138/3555925 #!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import select import termios import tty import pty from subprocess import Popen command = 'bash' # command = 'docker run -it --rm centos /bin/bash'.split() # save original tty setting then set it to raw mode old_tty = termios.tcgetattr(sys.stdin) tty.setraw(sys.stdin.fileno()) # open pseudo-terminal to interact with subprocess master_fd, slave_fd = pty.openpty() # use os.setsid() make it run in a new process group, or bash job control will not be enabled p = Popen(command, preexec_fn=os.setsid, stdin=slave_fd, stdout=slave_fd, stderr=slave_fd, universal_newlines=True) while p.poll() is None: r, w, e = select.select([sys.stdin, master_fd], [], []) if sys.stdin in r: d = os.read(sys.stdin.fileno(), 10240) os.write(master_fd, d) elif master_fd in r: o = os.read(master_fd, 10240) if o: os.write(sys.stdout.fileno(), o) # restore tty settings back termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
My problem is a bit different as I wanted to collect both stdout and stderr from a running process, but ultimately the same since I wanted to render the output in a widget as its generated. I did not want to resort to many of the proposed workarounds using Queues or additional Threads as they should not be necessary to perform such a common task as running another script and collecting its output. After reading the proposed solutions and python docs I resolved my issue with the implementation below. Yes it only works for POSIX as I'm using the select function call. I agree that the docs are confusing and the implementation is awkward for such a common scripting task. I believe that older versions of python have different defaults for Popen and different explanations so that created a lot of confusion. This seems to work well for both Python 2.7.12 and 3.5.2. The key was to set bufsize=1 for line buffering and then universal_newlines=True to process as a text file instead of a binary which seems to become the default when setting bufsize=1. class workerThread(QThread): def __init__(self, cmd): QThread.__init__(self) self.cmd = cmd self.result = None ## return code self.error = None ## flag indicates an error self.errorstr = "" ## info message about the error def __del__(self): self.wait() DEBUG("Thread removed") def run(self): cmd_list = self.cmd.split(" ") try: cmd = subprocess.Popen(cmd_list, bufsize=1, stdin=None , universal_newlines=True , stderr=subprocess.PIPE , stdout=subprocess.PIPE) except OSError: self.error = 1 self.errorstr = "Failed to execute " + self.cmd ERROR(self.errorstr) finally: VERBOSE("task started...") import select while True: try: r,w,x = select.select([cmd.stdout, cmd.stderr],[],[]) if cmd.stderr in r: line = cmd.stderr.readline() if line != "": line = line.strip() self.emit(SIGNAL("update_error(QString)"), line) if cmd.stdout in r: line = cmd.stdout.readline() if line == "": break line = line.strip() self.emit(SIGNAL("update_output(QString)"), line) except IOError: pass cmd.wait() self.result = cmd.returncode if self.result < 0: self.error = 1 self.errorstr = "Task terminated by signal " + str(self.result) ERROR(self.errorstr) return if self.result: self.error = 1 self.errorstr = "exit code " + str(self.result) ERROR(self.errorstr) return return ERROR, DEBUG and VERBOSE are simply macros that print output to the terminal. This solution is IMHO 99.99% effective as it still uses the blocking readline function, so we assume the sub process is nice and outputs complete lines. I welcome feedback to improve the solution as I am still new to Python.
I have created a library based on J. F. Sebastian's solution. You can use it. https://github.com/cenkalti/what
Working from J.F. Sebastian's answer, and several other sources, I've put together a simple subprocess manager. It provides the request non-blocking reading, as well as running several processes in parallel. It doesn't use any OS-specific call (that I'm aware) and thus should work anywhere. It's available from pypi, so just pip install shelljob. Refer to the project page for examples and full docs.
EDIT: This implementation still blocks. Use J.F.Sebastian's answer instead. I tried the top answer, but the additional risk and maintenance of thread code was worrisome. Looking through the io module (and being limited to 2.6), I found BufferedReader. This is my threadless, non-blocking solution. import io from subprocess import PIPE, Popen p = Popen(['myprogram.exe'], stdout=PIPE) SLEEP_DELAY = 0.001 # Create an io.BufferedReader on the file descriptor for stdout with io.open(p.stdout.fileno(), 'rb', closefd=False) as buffer: while p.poll() == None: time.sleep(SLEEP_DELAY) while '\n' in bufferedStdout.peek(bufferedStdout.buffer_size): line = buffer.readline() # do stuff with the line # Handle any remaining output after the process has ended while buffer.peek(): line = buffer.readline() # do stuff with the line
This solution uses the select module to "read any available data" from an IO stream. This function blocks initially until data is available, but then reads only the data that is available and doesn't block further. Given the fact that it uses the select module, this only works on Unix. The code is fully PEP8-compliant. import select def read_available(input_stream, max_bytes=None): """ Blocks until any data is available, then all available data is then read and returned. This function returns an empty string when end of stream is reached. Args: input_stream: The stream to read from. max_bytes (int|None): The maximum number of bytes to read. This function may return fewer bytes than this. Returns: str """ # Prepare local variables input_streams = [input_stream] empty_list = [] read_buffer = "" # Initially block for input using 'select' if len(select.select(input_streams, empty_list, empty_list)[0]) > 0: # Poll read-readiness using 'select' def select_func(): return len(select.select(input_streams, empty_list, empty_list, 0)[0]) > 0 # Create while function based on parameters if max_bytes is not None: def while_func(): return (len(read_buffer) < max_bytes) and select_func() else: while_func = select_func while True: # Read single byte at a time read_data = input_stream.read(1) if len(read_data) == 0: # End of stream break # Append byte to string buffer read_buffer += read_data # Check if more data is available if not while_func(): break # Return read buffer return read_buffer
I also faced the problem described by Jesse and solved it by using "select" as Bradley, Andy and others did but in a blocking mode to avoid a busy loop. It uses a dummy Pipe as a fake stdin. The select blocks and wait for either stdin or the pipe to be ready. When a key is pressed stdin unblocks the select and the key value can be retrieved with read(1). When a different thread writes to the pipe then the pipe unblocks the select and it can be taken as an indication that the need for stdin is over. Here is some reference code: import sys import os from select import select # ------------------------------------------------------------------------- # Set the pipe (fake stdin) to simulate a final key stroke # which will unblock the select statement readEnd, writeEnd = os.pipe() readFile = os.fdopen(readEnd) writeFile = os.fdopen(writeEnd, "w") # ------------------------------------------------------------------------- def getKey(): # Wait for stdin or pipe (fake stdin) to be ready dr,dw,de = select([sys.__stdin__, readFile], [], []) # If stdin is the one ready then read it and return value if sys.__stdin__ in dr: return sys.__stdin__.read(1) # For Windows use ----> getch() from module msvcrt # Must finish else: return None # ------------------------------------------------------------------------- def breakStdinRead(): writeFile.write(' ') writeFile.flush() # ------------------------------------------------------------------------- # MAIN CODE # Get key stroke key = getKey() # Keyboard input if key: # ... do your stuff with the key value # Faked keystroke else: # ... use of stdin finished # ------------------------------------------------------------------------- # OTHER THREAD CODE breakStdinRead()
Try wexpect, which is the windows alternative of pexpect. import wexpect p = wexpect.spawn('myprogram.exe') p.stdout.readline('.') // regex pattern of any character output_str = p.after()
Here is a module that supports non-blocking reads and background writes in python: https://pypi.python.org/pypi/python-nonblock Provides a function, nonblock_read which will read data from the stream, if available, otherwise return an empty string (or None if the stream is closed on the other side and all possible data has been read) You may also consider the python-subprocess2 module, https://pypi.python.org/pypi/python-subprocess2 which adds to the subprocess module. So on the object returned from "subprocess.Popen" is added an additional method, runInBackground. This starts a thread and returns an object which will automatically be populated as stuff is written to stdout/stderr, without blocking your main thread. Enjoy!
subprocess.Popen with shell=True hangs on 'time <...>'
I'm using this code, which is supposed to be transitional for python2 to python3 porting phase (I know there are third-party libs for run, I want to implement my own to collect experience) def run(args, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, timeout=None, encoding=None, **popen_kwargs): # create stderr and stdout pipes, if capture_output is true if capture_output: _stderr = subprocess.PIPE _stdout = subprocess.PIPE else: _stdout, _stderr = stdout, stderr # if input is given, create stdin as pipe, where input will be passed into if input is not None: _stdin = subprocess.PIPE else: _stdin = stdin # this starts the process. python2 did not have 'encoding' if sys.version_info.major >= 3: proc = subprocess.Popen(args, stdin=_stdin, stdout=_stdout, stderr=_stderr, encoding=encoding, **popen_kwargs) else: proc = subprocess.Popen( args, stdin=_stdin, stdout=_stdout, stderr=_stderr, **popen_kwargs) # run a background timer to interrupt 'communicate', if necessary if timeout is not None: def cancel(): try: proc.terminate() except OSError: # an exception here means that the process is gone already pass cancel_timer = Timer(timeout, cancel) cancel_timer.start() # special case for python2 for which we allow passing 'unicode' if an encoding is used if input is not None and sys.version_info.major < 3: if type(input) == unicode: import codecs input = codecs.encode(input, encoding) (stdoutoutput, stderroutput) = proc.communicate(input) # check timeout scenario if timeout is not None: if not cancel_timer.is_alive(): raise TimeoutExpired(args, timeout, stdoutoutput, stdoutoutput, stderroutput) else: cancel_timer.cancel() cancel_timer.join() # on python2, outputs will always be 'str', which is fine with us, as it's the union of # str and bytes return CompletedProcess(args, proc.poll(), stdoutoutput, stderroutput) However, the code blocks indefinitely within communicate whenever I try to execute anything with the shell builtin time prefixed and capture_output on both python2 and python3. It must be something really stupid. >>> run("time sleep 5m", shell=True, capture_output=True, timeout=1) ... (^ C to stop it) >>> run("sleep 5m", shell=True, capture_output=True, timeout=1) zsubprocess.TimeoutExpired: sleep 5m: Timeout after 1 seconds I do not understand why that is the case.
As it turned out, subprocess does not execute a shell when the command is just sleep 5m, but only when it is time sleep 5m (more accurately: sh optimizes its -c and apparently execs it instead of forking). When I executed proc.terminate(), it only terminated the wrapper shell, but not the sleep, which became a child of the child subreaper (init here). And possibly (I guess) communicate wait for the pipes to yield EOF to collect outstanding data, which won't happen unless the sleep terminates. The following solution was taken from https://stackoverflow.com/a/25134985/34509 . Instead of proc.kill, do process = psutil.Process(proc.pid) if proc.poll() is None: for child in process.children(recursive=True): child.kill() process.kill() There's a small chance that we could race with the process, where it could create new processes before we could kill them, but at least simple cases work now!
asynchronous subprocess Popen python 3.5
I am trying to asynchronously run the Popen command from subprocess, so that I can run other stuff in the background. import subprocess import requests import asyncio import asyncio.subprocess async def x(message): if len(message.content.split()) > 1: #output = asyncio.create_subprocess_shell(message.content[3:], shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT) output = subprocess.Popen(message.content[3:], shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT) return output.communicate()[0].decode('utf-8') I have tried to understand https://docs.python.org/3/library/asyncio-subprocess.html but i am not sure what a protocol factory is.
When I came to this question, I expected the answer to really use asyncio for interprocess communication. I have found the following resource useful: https://github.com/python/asyncio/blob/master/examples/child_process.py and below is my simplified example (using 3.5+ async/await syntax), which reads lines and outputs them sorted: import asyncio from subprocess import Popen, PIPE async def connect_write_pipe(file): """Return a write-only transport wrapping a writable pipe""" loop = asyncio.get_event_loop() transport, _ = await loop.connect_write_pipe(asyncio.Protocol, file) return transport async def connect_read_pipe(file): """Wrap a readable pipe in a stream""" loop = asyncio.get_event_loop() stream_reader = asyncio.StreamReader(loop=loop) def factory(): return asyncio.StreamReaderProtocol(stream_reader) transport, _ = await loop.connect_read_pipe(factory, file) return stream_reader, transport async def main(loop): # start subprocess and wrap stdin, stdout, stderr p = Popen(['/usr/bin/sort'], stdin=PIPE, stdout=PIPE, stderr=PIPE) stdin = await connect_write_pipe(p.stdin) stdout, stdout_transport = await connect_read_pipe(p.stdout) stderr, stderr_transport = await connect_read_pipe(p.stderr) # interact with subprocess name = {stdout: 'OUT', stderr: 'ERR'} registered = { asyncio.Task(stderr.read()): stderr, asyncio.Task(stdout.read()): stdout } to_sort = b"one\ntwo\nthree\n" stdin.write(to_sort) stdin.close() # this way we tell we do not have anything else # get and print lines from stdout, stderr timeout = None while registered: done, pending = await asyncio.wait( registered, timeout=timeout, return_when=asyncio.FIRST_COMPLETED) if not done: break for f in done: stream = registered.pop(f) res = f.result() if res != b'': print(name[stream], res.decode('ascii').rstrip()) registered[asyncio.Task(stream.read())] = stream timeout = 0.0 stdout_transport.close() stderr_transport.close() if __name__ == '__main__': loop = asyncio.get_event_loop() try: loop.run_until_complete(main(loop)) finally: loop.close() NB: without taking special measures, the amount of data to be written into the pipe is limited. In my system it was possible to write just over 700000 bytes before using up pipe buffers. There are also other examples there, using create_subprocess_shell. I have not yet used asyncio in real projects, so improvements' suggestions in the comments are welcome.
It's the right way to go...! Use async/await Tested it on Python - 3.X [Windows, MacOS] import asyncio from asyncio.subprocess import PIPE, STDOUT import subprocess import signal def signal_handler(signal, frame): loop.stop() client.close() sys.exit(0) async def run_async(loop = ''): cmd = 'sudo long_running_cmd --opt1=AAAA --opt2=BBBB' print ("[INFO] Starting script...") await asyncio.create_subprocess_shell(cmd1, stdin = PIPE, stdout = PIPE, stderr = STDOUT) print("[INFO] Script is complete.") loop = asyncio.get_event_loop() signal.signal(signal.SIGINT, signal_handler) tasks = [loop.create_task(run_async())] wait_tasks = asyncio.wait(tasks) loop.run_until_complete(wait_tasks) loop.close() Core logic: process = await asyncio.create_subprocess_shell(cmd1, stdin = PIPE, stdout PIPE, stderr = STDOUT) await process.wait()
I eventually found the answer to my question, which utilizes async. http://pastebin.com/Zj8SK1CG
Tested with python 3.5. Just ask if you have questions. import threading import time import subprocess import shlex from sys import stdout # Only data wihtin a class are actually shared by the threads. # Let's use a class as communicator (there could be problems if you have more than # a single thread) class Communicator(object): counter = 0 stop = False arg = None result = None # Here we can define what you want to do. There are other methods to do that # but this is the one I prefer. class ThreadedFunction(threading.Thread): def run(self, *args, **kwargs): super().run() command = c.arg # Here what you want to do... command = shlex.split(command) print(time.time()) # this is just to check that the command (sleep 5) is executed output = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() print('\n',time.time()) c.result = output if c.stop: return None # This is useful only within loops within threads # Create a class instance c = Communicator() c.arg = 'time sleep 5' # Here I used the 'time' only to have some output # Create the thread and start it t = ThreadedFunction() t.start() # Start the thread and do something else... # ...for example count the seconds in the mean time.. try: for j in range(100): c.counter += 1 stdout.write('\r{:}'.format(c.counter)) stdout.flush() time.sleep(1) if c.result != None: print(c.result) break except: c.stop = True
This one is much simpler, I found it after the other reply that could, anyway, be interesting... so I left it. import time import subprocess import shlex from sys import stdout command = 'time sleep 5' # Here I used the 'time' only to have some output def x(command): cmd = shlex.split(command) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return p # Start the subprocess and do something else... p = x(command) # ...for example count the seconds in the mean time.. try: # This take care of killing the subprocess if problems occur for j in range(100): stdout.write('\r{:}'.format(j)) stdout.flush() time.sleep(1) if p.poll() != None: print(p.communicate()) break except: p.terminate() # or p.kill() The asynchronism is evident from the fact that the python script prints the counter value on the stdout while the background process runs the sleep command. The fact that the python script exit after ~5sec printing the output of the bash time command printing the counter in the meanwhile is an evidence that the script works.
How do I get data from a subprocess PIPE while the subprocess is running in Python?
I've got a program on Windows that calls a bunch of subprocesses, and displays the results in a GUI. I'm using PyQt for the GUI, and the subprocess module to run the programs. I've got the following WorkerThread, that spawns a subthread for each shell command devoted to reading the process stdout and printing the results (later I'll wire it up to the GUI). This all works. Except proc.stdout.read(1) never returns until after the subprocess has completed. This is a big problem, since some of these subprocesses can take 15-20 minutes to run, and I need to display results as they're running. What do I need to do to get the pipe working while the subprocess is running? class WorkerThread(QtCore.QThread): def run(self): def sh(cmd, cwd = None): proc = subprocess.Popen(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, stdin = subprocess.PIPE, cwd = cwd, env = os.environ) proc.stdin.close() class ReadStdOutThread(QtCore.QThread): def run(_self): s = '' while True: if self.request_exit: return b = proc.stdout.read(1) if b == '\n': print s s = '' continue if b: s += b continue if s: print s return thread = ReadStdOutThread() thread.start() retcode = proc.wait() if retcode: raise subprocess.CalledProcessError(retcode, cmd) return 0 FWIW: I rewrote the whole thing using QProcess, and I see the exact same problem. The stdout receives no data, until the underlying process has returned. Then I get everything all at once.
If you know how long will be the the lines of command's output you can poll on the stdout PIPE of the process. An example of what I mean: import select import subprocess import threading import os # Some time consuming command. command = 'while [ 1 ]; do sleep 1; echo "Testing"; done' # A worker thread, not as complex as yours, just to show my point. class Worker(threading.Thread): def __init__(self): super(Worker, self).__init__() self.proc = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT ) def run(self): self.proc.communicate() def get_proc(self): # The proc is needed for ask him for his # output file descriptor later. return self.proc if __name__ == '__main__': w = Worker() w.start() proc = w.get_proc() pollin = select.poll() pollin.register(proc.stdout, select.POLLIN) while ( 1 ): events = pollin.poll() for fd, event in events: if event == select.POLLIN: # This is the main issue of my idea, # if you don't know the length of lines # that process ouput, this is a problem. # I put 7 since I know the word "Testing" have # 7 characters. print os.read(fd, 7) Maybe this is not exactly what you're looking for, but I think it give you a pretty good idea of what to do to solve your problem. EDIT: I think I've just found what you need Streaming stdout from a Python subprocess in Python.
How to limit program's execution time when using subprocess?
I want to use subprocess to run a program and I need to limit the execution time. For example, I want to kill it if it runs for more than 2 seconds. For common programs, kill() works well. But if I try to run /usr/bin/time something, kill() can’t really kill the program. My code below seems doesn’t work well. The program is still running. import subprocess import time exec_proc = subprocess.Popen("/usr/bin/time -f \"%e\\n%M\" ./son > /dev/null", stdout = subprocess.PIPE, stderr = subprocess.STDOUT, shell = True) max_time = 1 cur_time = 0.0 return_code = 0 while cur_time <= max_time: if exec_proc.poll() != None: return_code = exec_proc.poll() break time.sleep(0.1) cur_time += 0.1 if cur_time > max_time: exec_proc.kill()
If you're using Python 2.6 or later, you can use the multiprocessing module. from multiprocessing import Process def f(): # Stuff to run your process here p = Process(target=f) p.start() p.join(timeout) if p.is_alive(): p.terminate() Actually, multiprocessing is the wrong module for this task since it is just a way to control how long a thread runs. You have no control over any children the thread may run. As singularity suggests, using signal.alarm is the normal approach. import signal import subprocess def handle_alarm(signum, frame): # If the alarm is triggered, we're still in the exec_proc.communicate() # call, so use exec_proc.kill() to end the process. frame.f_locals['self'].kill() max_time = ... stdout = stderr = None signal.signal(signal.SIGALRM, handle_alarm) exec_proc = subprocess.Popen(['time', 'ping', '-c', '5', 'google.com'], stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) signal.alarm(max_time) try: (stdout, stderr) = exec_proc.communicate() except IOError: # process was killed due to exceeding the alarm finally: signal.alarm(0) # do stuff with stdout/stderr if they're not None
do it like so in your command line: perl -e 'alarm shift #ARGV; exec #ARGV' <timeout> <your_command> this will run the command <your_command> and terminate it in <timeout> second. a dummy example : # set time out to 5, so that the command will be killed after 5 second command = ['perl', '-e', "'alarm shift #ARGV; exec #ARGV'", "5"] command += ["ping", "www.google.com"] exec_proc = subprocess.Popen(command) or you can use the signal.alarm() if you want it with python but it's the same.
I use os.kill() but am not sure if it works on all OSes. Pseudo code follows, and see Doug Hellman's page. proc = subprocess.Popen(['google-chrome']) os.kill(proc.pid, signal.SIGUSR1)</code>