#!/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()