2011-07-24 20 views
27

रखते हुए मैं एक पायथन स्क्रिप्ट के उपप्रोसेस' stdout और stdin को उसी फ़ाइल में निर्देशित करना चाहता हूं। मुझे नहीं पता कि दो स्रोतों से लाइनों को अलग-अलग कैसे बनाया जा सकता है? (उदाहरण के लिए एक विस्मयादिबोधक चिह्न के साथ stderr से लाइनों को उपसर्ग करें।)एक पाइथन लिपि के सबप्रोसेस 'stdout और stderr को विलय करते हुए उन्हें अलग-अलग

मेरे विशेष मामले में उपप्रोसेस की लाइव निगरानी की कोई आवश्यकता नहीं है, निष्पादित पायथन स्क्रिप्ट इसके निष्पादन के अंत तक प्रतीक्षा कर सकती है।

+1

क्या होगा यदि प्रोग्राम के कुछ स्टडआउट विस्मयादिबोधक बिंदु से शुरू होते हैं? – robert

+0

क्या सभी स्टडआउट को डंप करना और फिर सभी stderr को उसी फ़ाइल में डंप करना ठीक है, या आप उन्हें अंतःस्थापित करना चाहते हैं? – robert

+0

मैं इसे अंतःस्थापित करना चाहता हूं। – beemtee

उत्तर

0

आप कमांड निष्पादन के बाद फ़ाइल में stdout/err लिख सकते हैं। नीचे दिए गए उदाहरण में मैं पिकलिंग का उपयोग करता हूं, इसलिए मुझे यकीन है कि मैं stdout/err के बीच अंतर करने के लिए किसी भी विशेष पार्सिंग के बिना पढ़ सकूंगा और किसी बिंदु पर मैं बाहर निकलने के लिए और कमांड को कम कर सकता हूं।

import subprocess 
import cPickle 

command = 'ls -altrh' 
outfile = 'log.errout' 
pipe = subprocess.Popen(command, stdout = subprocess.PIPE, 
         stderr = subprocess.PIPE, shell = True) 
stdout, stderr = pipe.communicate() 

f = open(outfile, 'w') 
cPickle.dump({'out': stdout, 'err': stderr},f) 
f.close() 
+2

ओपी चाहता है कि stdout/err * interleaved * । – jfs

+0

@ जे-एफ-सेबेस्टियन: सिंको ने इसे पहले ही पोस्ट करने के 5 मिनट बाद टिप्पणी में अपरिपक्व हो गया। – tutuDajuju

9

आप मोटे तौर पर एक ही आदेश क्या तुम करोगी कि प्राप्त करने के लिए अगर आप इस प्रक्रिया के सहभागी तो भाग गया है कि तुम क्या खोल और चुनाव stdin/stdout करता है और लिखने की ज़रूरत आदेश है कि वे चुनाव में बिछा चाहते हैं।

यहां कुछ कोड है जो आप चाहते हैं कि लाइनों के साथ कुछ करता है - इस मामले में एक लॉगजर जानकारी/त्रुटि धाराओं के लिए stdout/stderr भेजना।

tsk = subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE) 

poll = select.poll() 
poll.register(tsk.stdout,select.POLLIN | select.POLLHUP) 
poll.register(tsk.stderr,select.POLLIN | select.POLLHUP) 
pollc = 2 

events = poll.poll() 
while pollc > 0 and len(events) > 0: 
    for event in events: 
    (rfd,event) = event 
    if event & select.POLLIN: 
     if rfd == tsk.stdout.fileno(): 
     line = tsk.stdout.readline() 
     if len(line) > 0: 
      logger.info(line[:-1]) 
     if rfd == tsk.stderr.fileno(): 
     line = tsk.stderr.readline() 
     if len(line) > 0: 
      logger.error(line[:-1]) 
    if event & select.POLLHUP: 
     poll.unregister(rfd) 
     pollc = pollc - 1 
    if pollc > 0: events = poll.poll() 
tsk.wait() 
+0

आपके पास एक कॉपी-पेस्ट त्रुटि है - आप हमेशा 'tsk.stdout' से पढ़ते हैं। इसके अलावा, आप 'आरएफडी, घटनाओं में घटना के लिए' कर सकते हैं: '। अन्यथा, +1। – robert

+0

मैंने आपके प्रोग्राम को चलाया ([कुछ संशोधनों] के साथ (http://pastebin.com/gScsiU9S)) [यह अन्य प्रोग्राम] (http://pastebin.com/yHGx4wWn) को कॉल करना और आउटपुट [यह] था (http: //pastebin.com/VkjN3T9P)। Stderr का उत्पादन हमेशा प्रस्तुत क्यों किया जाता है? मैं मैक ओएस एक्स का उपयोग कर रहा हूं। – brandizzi

+0

मेरे मामले में सही ऑर्डरिंग इस पूरे 'सेट-अलग' चीज़ से अधिक महत्वपूर्ण है, इसलिए यह दिलचस्प होगा कि इस मतदान दृष्टिकोण में कितना बदलाव आएगा? – beemtee

1

मैं सुझाव है कि आप अपने खुद के संचालकों लिखते हैं, की तरह (परीक्षण नहीं किया है, मैं तुम्हें विचार को पकड़ने की उम्मीद है) कुछ:

class my_buffer(object): 
    def __init__(self, fileobject, prefix): 
     self._fileobject = fileobject 
     self.prefix = prefix 
    def write(self, text): 
     return self._fileobject.write('%s %s' % (self.prefix, text)) 
    # delegate other methods to fileobject if necessary 

log_file = open('log.log', 'w') 
my_out = my_buffer(log_file, 'OK:') 
my_err = my_buffer(log_file, '!!!ERROR:') 
p = subprocess.Popen(command, stdout=my_out, stderr=my_err, shell=True) 
+1

आपको 'self.prefix' सेट करने की आवश्यकता है। – robert

+0

सही, धन्यवाद – Guard

+0

मेरी समझ में पॉपन फ़ाइल डिस्क्रिप्टर को _stdout_ और _stdin_ के रूप में अपेक्षा करता है, और इसके बारे में बहुत उदार नहीं है। क्या मै गलत हु? – beemtee

28
tsk = subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.STDOUT) 

subprocess.STDOUT एक विशेष ध्वज है कि सभी मार्ग को उपप्रक्रिया बताता है stderr आउटपुट stdout, इस प्रकार अपनी दो धाराओं को मिलाकर।

बीटीडब्ल्यू, विंडोज़ में कोई मतदान() नहीं है। उपप्रोसेस केवल फ़ाइल हैंडल नंबर का उपयोग करता है, और आपकी फ़ाइल आउटपुट ऑब्जेक्ट की लेखन विधि को कॉल नहीं करता है। इसलिए यहाँ यह है

logfile = open(logfilename, 'w') 

while tsk.poll() is None: 
    line = tsk.stdout.readline() 
    logfile.write(line) 
+3

आपने प्रश्न के भाग "दो स्रोतों से अलग-अलग लाइनों को बनाने के तरीके" को याद किया होगा। 'subprocess.STDOUT' stdout/err को अलग करने का कोई तरीका नहीं छोड़ता है। – jfs

+2

अभी भी +1 है क्योंकि इस विषय पर यह जानने के लिए एक उपयोगी बात है –

11

मुझे लगने हाल ही में इस समस्या से निपटने के लिए हो रही है, और यह कुछ समय लिया कुछ मैं सही ढंग से ज्यादातर मामलों में काम किया महसूस पाने के लिए,:

उत्पादन पर कब्जा करने, की तरह कुछ करना ! (यह एक पायथन लॉगर के माध्यम से आउटपुट को प्रोसेस करने का अच्छा साइड इफेक्ट भी है, जिसे मैंने देखा है स्टैक ओवरफ्लो पर एक और आम सवाल है)।

import sys 
import logging 
import subprocess 
from threading import Thread 

logging.basicConfig(stream=sys.stdout,level=logging.INFO) 
logging.addLevelName(logging.INFO+2,'STDERR') 
logging.addLevelName(logging.INFO+1,'STDOUT') 
logger = logging.getLogger('root') 

pobj = subprocess.Popen(['python','-c','print 42;bargle'], 
    stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

def logstream(stream,loggercb): 
    while True: 
     out = stream.readline() 
     if out: 
      loggercb(out.rstrip())  
     else: 
      break 

stdout_thread = Thread(target=logstream, 
    args=(pobj.stdout,lambda s: logger.log(logging.INFO+1,s))) 

stderr_thread = Thread(target=logstream, 
    args=(pobj.stderr,lambda s: logger.log(logging.INFO+2,s))) 

stdout_thread.start() 
stderr_thread.start() 

while stdout_thread.isAlive() and stderr_thread.isAlive(): 
    pass 

यहाँ आउटपुट है:

यहाँ कोड है

STDOUT:root:42 
STDERR:root:Traceback (most recent call last): 
STDERR:root: File "<string>", line 1, in <module> 
STDERR:root:NameError: name 'bargle' is not defined 

आप जो चाहें करें करने के लिए उपप्रक्रिया कॉल जगह ले सकता है, मैं सिर्फ एक आदेश के साथ अजगर चल चुना है कि मैं पता था कि stdout और stderr दोनों प्रिंट करेंगे। कुंजी बिट एक अलग थ्रेड में stderr और stdout प्रत्येक पढ़ रहा है। अन्यथा आप एक को पढ़ने पर अवरुद्ध हो सकते हैं जबकि डेटा दूसरे पर पढ़ने के लिए तैयार है।

+0

यह गैर-इंटरैक्टिव मोड (जैसा कि आमतौर पर है) में बच्चे उप-प्रोसेसिस ब्लॉक-बफरिंग का उपयोग करता है, तो यह ठीक से अंतःस्थापित आउटपुट का उत्पादन नहीं करेगा। [मैंने इसे पीटीआई का उपयोग करके ठीक करने की कोशिश की है] (http://stackoverflow.com/a/11709034/4279) – jfs

+3

रोजन के जवाब के विपरीत यह विंडोज़ पर काम करता है, जो अच्छा है। हालांकि मुझे लगता है कि 'stdout_thread.isAlive() और stderr_thread.isAlive()' अक्षम है और 'stdout_thread.join()' stderr_thread.join() '' 'के साथ प्रतिस्थापित किया जा सकता है। –

2

फिलहाल अन्य सभी उत्तरों बच्चे उपप्रोसेस पक्ष पर बफरिंग को संभाल नहीं पाते हैं यदि उपप्रोसेसर एक पायथन स्क्रिप्ट नहीं है जो -u ध्वज स्वीकार करता है। "Q: Why not just use a pipe (popen())?" in the pexpect documentation देखें।

कुछ सी stdio- आधारित (FILE*) प्रोग्राम के लिए -u ध्वज अनुकरण करने के लिए आप stdbuf कोशिश कर सकते हैं।

आप उपेक्षा अगर यह आपका उत्पादन ठीक से interleaved नहीं किया जाएगा और ऐसा दिखाई देगा: भी

stderr 
stderr 
...large block of stdout including parts that are printed before stderr... 

आप निम्न क्लाइंट प्रोग्राम के साथ यह कोशिश कर सकते के साथ/अंतर नोटिस -u ध्वज के बिना (['stdbuf', '-o', 'L', 'child_program'] उत्पादन ठीक करता है):

#!/usr/bin/env python 
from __future__ import print_function 
import random 
import sys 
import time 
from datetime import datetime 

def tprint(msg, file=sys.stdout): 
    time.sleep(.1*random.random()) 
    print("%s %s" % (datetime.utcnow().strftime('%S.%f'), msg), file=file) 

tprint("stdout1 before stderr") 
tprint("stdout2 before stderr") 
for x in range(5): 
    tprint('stderr%d' % x, file=sys.stderr) 
tprint("stdout3 after stderr") 

लिनक्स पर आप pty का उपयोग करते हैं उपप्रक्रिया सहभागी चलाता है जैसे के रूप में ही व्यवहार प्राप्त करने के लिए कर सकता है, यहाँ एक संशोधित @T.Rojan's answer है:

import logging, os, select, subprocess, sys, pty 

logging.basicConfig(level=logging.INFO) 
logger = logging.getLogger(__name__) 

master_fd, slave_fd = pty.openpty() 
p = subprocess.Popen(args,stdout=slave_fd, stderr=subprocess.PIPE, close_fds=True) 
with os.fdopen(master_fd) as stdout: 
    poll = select.poll() 
    poll.register(stdout, select.POLLIN) 
    poll.register(p.stderr,select.POLLIN | select.POLLHUP) 

    def cleanup(_done=[]): 
     if _done: return 
     _done.append(1) 
     poll.unregister(p.stderr) 
     p.stderr.close() 
     poll.unregister(stdout) 
     assert p.poll() is not None 

    read_write = {stdout.fileno(): (stdout.readline, logger.info), 
        p.stderr.fileno(): (p.stderr.readline, logger.error)} 
    while True: 
     events = poll.poll(40) # poll with a small timeout to avoid both 
           # blocking forever and a busy loop 
     if not events and p.poll() is not None: 
      # no IO events and the subprocess exited 
      cleanup() 
      break 

     for fd, event in events: 
      if event & select.POLLIN: # there is something to read 
       read, write = read_write[fd] 
       line = read() 
       if line: 
        write(line.rstrip()) 
      elif event & select.POLLHUP: # free resources if stderr hung up 
       cleanup() 
      else: # something unexpected happened 
       assert 0 
sys.exit(p.wait()) # return child's exit code 

यह मानता है कि stderr हमेशा unbuffered/line-buffered और stdout एक इंटरैक्टिव मोड में लाइन-buffered है। केवल पूर्ण लाइनें पढ़ी जाती हैं। आउटपुट में गैर-समाप्त लाइनें होने पर प्रोग्राम ब्लॉक हो सकता है।

संबंधित मुद्दे