2012-07-02 6 views
1

파이썬 스크립트에서 bash 쉘 세션을 랩핑하여 파이썬이 데이터베이스에 stdout과 stderr를 저장할 수있게하고 때때로 stdin에 쓸 수 있습니까?Bash 쉘 IO를 파이썬에서 매끄럽게 감싸는 방법

티 프로세스와 같은 파이썬 클래스를 사용하여 IO를 리디렉션하려고했지만 fileno를 사용하여 파이썬을 완전히 무시한 것처럼 보입니다.

shell.py :

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) 

예컨대 python shell.py ping google.com을 실행하면 올바른 명령이 실행되고 출력이 표시되지만 Python은 stdout을 결코 보지 못합니다.

+2

다음을보십시오. http://stackoverflow.com/questions/616645/how-do-i-duplicate-sys-stdout-to-a-log-file-in-python 그리고이 순수한 python 대답 같은 질문 : http://stackoverflow.com/questions/616645/how-do-i-duplicate-sys-stdout-to-a-log-file-in-python/3423392#3423392 – snies

+0

@snies, 감사. 그게 내 질문의 절반 (stdout과 stdin)을 덮을 지 모르지만, 나는 그것을 조사 할 것이다. – Cerin

+0

@snies 더 자세히 살펴보면 대부분의 응답은 임의의 수의 하위 프로세스에서 표준 로깅을 처리하는 것으로 보입니다. 대부분의 솔루션은 예제 코드와 동일한 오류가있는 것으로 보입니다. 즉 파이썬 write() 메소드는 결코 호출되지 않을 것이다. – Cerin

답변

1
#!/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,\n" + 
           "Aleph-null bottles of beer,\n" + 
           "Take one down and pass it around,\n" + 
           "Aleph-null bottles of beer on the wall.\n") 
     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() 

Thats 트렁킹 방식으로 명령 IO를 프로토콜로 처리합니다. StringIO로 스크립트를 복잡하게 만드시겠습니까? Popen.communicate() 메소드를 점검하십시오. stdin/stdout은 파일 디스크립터이며, 출력이 길어지면 버퍼가 오버플로되므로 병렬로 읽어야합니다. 거대한 데이터를 스트리밍하려는 경우 Twisted 방식을 사용하거나, Popen 방식으로 stdout을 읽는 별도의 스레드를 실행하는 경우 줄을 긋고 DB에 즉시 넣을 수 있습니다. Twisted howto on process protocol.

관련 문제