#!/usr/bin/env python # coding:utf-8 from __future__ import absolute_import, print_function import os import fcntl import select import subprocess from threading import Timer def make_nonblock(fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) class Shell(object): TIMEOUT_MSG = "" def __init__(self, cmd): PIPE = subprocess.PIPE self.proc = subprocess.Popen( cmd, stdout=PIPE, stdin=PIPE, stderr=PIPE, shell=True) make_nonblock(self.proc.stdout) make_nonblock(self.proc.stderr) def read(self, timeout=None): timer = None ret = {} out_ret = [] err_ret = [] if timeout: timer = Timer(timeout, self._kill_proc, [self.proc, ]) timer.setDaemon(True) timer.start() while True: out = self._read(self.proc.stdout) err = self._read(self.proc.stderr) out_ret.append(out) err_ret.append(err) if self.proc.poll() is not None: break out = self._read(self.proc.stdout) err = self._read(self.proc.stderr) out_ret.append(out) err_ret.append(err) ret["out"] = "".join(out_ret) ret["err"] = "".join(err_ret) + self.TIMEOUT_MSG ret["status"] = self.proc.returncode if timer: timer.cancel() timer.join() return ret def _read(self, fd): out = "" if fd is None or fd.closed: return out rd, _, _ = select.select([fd], [], [], 0) if rd: out = fd.readline() return out def _kill_proc(self, proc): if proc: try: proc.kill() self.TIMEOUT_MSG = "timeout" except OSError: pass def main(): shell = Shell("sleep 2;ls") print(shell.read(1)) shell = Shell("ls; sleep 2") print(shell.read(1)) shell = Shell("ls") print(shell.read()) if __name__ == "__main__": main()