How to seamlessly wrap Bash shell IO in Python
如何在Python脚本中包装bash shell会话,以便Python可以将stdout和stderr存储到数据库中,偶尔写入stdin?
我尝试使用带有类似tee的Python类的子进程来重定向IO,但它似乎使用fileno完全绕过Python。
shell.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import os import sys from StringIO import StringIO from subprocess import Popen, PIPE class TeeFile(StringIO): def __init__(self, file, auto_flush=False): #super(TeeFile, self).__init__() StringIO.__init__(self) self.file = file self.auto_flush = auto_flush self.length = 0 def write(self, s): print 'writing' # This is never called!!! self.length += len(s) self.file.write(s) #super(TeeFile, self).write(s) StringIO.write(self, s) if self.auto_flush: self.file.flush() def flush(self): self.file.flush() StringIO.flush(self) def fileno(self): return self.file.fileno() cmd = ' '.join(sys.argv[1:]) stderr = TeeFile(sys.stderr, True) stdout = TeeFile(sys.stdout, True) p = Popen(cmd, shell=True, stdin=PIPE, stdout=stdout, stderr=stderr, close_fds=True) |
例如 运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet import protocol from twisted.internet import reactor import re class MyPP(protocol.ProcessProtocol): def __init__(self, verses): self.verses = verses self.data ="" def connectionMade(self): print"connectionMade!" for i in range(self.verses): self.transport.write("Aleph-null bottles of beer on the wall, " + "Aleph-null bottles of beer, " + "Take one down and pass it around, " + "Aleph-null bottles of beer on the wall. ") self.transport.closeStdin() # tell them we're done def outReceived(self, data): print"outReceived! with %d bytes!" % len(data) self.data = self.data + data def errReceived(self, data): print"errReceived! with %d bytes!" % len(data) def inConnectionLost(self): print"inConnectionLost! stdin is closed! (we probably did it)" def outConnectionLost(self): print"outConnectionLost! The child closed their stdout!" # now is the time to examine what they wrote #print"I saw them write:", self.data (dummy, lines, words, chars, file) = re.split(r'\s+', self.data) print"I saw %s lines" % lines def errConnectionLost(self): print"errConnectionLost! The child closed their stderr." def processExited(self, reason): print"processExited, status %d" % (reason.value.exitCode,) def processEnded(self, reason): print"processEnded, status %d" % (reason.value.exitCode,) print"quitting" reactor.stop() pp = MyPP(10) reactor.spawnProcess(pp,"wc", ["wc"], {}) reactor.run() |
这是将命令IO作为协议处理的Twisted方式。 顺便说一下,你的脚本也很复杂。 而是检查Popen.communicate()方法。 请注意,stdin / stdout是文件描述符,需要并行读取,因为如果输出更长,它们的缓冲区将会溢出。 如果你想通过这个流式传输大量数据,而是使用那种Twisted方式,或者在Popen方式激活单独的线程来读取stdout的情况下可以通过行并立即将它们放到DB中。
关于过程协议的扭曲的howto。