2012-06-22 20 views
5

कैप्चर करते समय चुनिंदा और पीटीआई लटकते हुए उपप्रोसेस का उपयोग करके मैं एक पायथन प्रोग्राम लिखने की कोशिश कर रहा हूं जो अन्य प्रोग्राम्स के साथ बातचीत करने में सक्षम है। इसका मतलब है कि stdin भेजना और stdout डेटा प्राप्त करना। मैं pexpect का उपयोग नहीं कर सकता (हालांकि यह निश्चित रूप से कुछ डिजाइन प्रेरित)।आउटपुट

  1. एक प्राइवेट उपप्रक्रिया के stdout
  2. लूप को उपप्रक्रिया बाहर निकलता है जब तक subprocess.poll
    • की जाँच करके संलग्न जब डेटा stdout कि डेटा लिखने में उपलब्ध है: प्रक्रिया मैं अभी उपयोग कर रहा हूँ यह है तुरंत वर्तमान stdout के लिए।
  3. समाप्त करें!

मैं कुछ कोड (नीचे) प्रोटोटाइप कर रहा हूं जो काम करता है लेकिन ऐसा लगता है कि मुझे एक दोष है जो मुझे परेशान कर रहा है। बाल प्रक्रिया पूरी होने के बाद, यदि मैं select.select का उपयोग करते समय टाइमआउट निर्दिष्ट नहीं करता हूं तो मूल प्रक्रिया लटकती है। मैं वास्तव में एक टाइमआउट सेट नहीं करना पसंद करूंगा। यह बस थोड़ा गंदा लगता है। हालांकि, इस मुद्दे के आसपास आने के अन्य सभी तरीकों से काम नहीं लगता है। और pty.openpty के बजाय os.execv और pty.fork का उपयोग करके Pexpect इसके आसपास मिल रहा है, जो समाधान मुझे पसंद नहीं है। क्या मैं कुछ गलत कर रहा हूं कि मैं उपप्रजाति के जीवन की जांच कैसे करता हूं? क्या मेरा दृष्टिकोण गलत है?

मैं जिस कोड का उपयोग कर रहा हूं वह नीचे है। मैं इसे मैक ओएस एक्स 10.6.8 पर उपयोग कर रहा हूं, लेकिन मुझे इसे उबंटू 12.04 पर भी काम करने की ज़रूरत है।

import subprocess 
import select 
import pty 
import os 
import sys 

def main(): 
    master, slave = pty.openpty() 

    process = subprocess.Popen(['python', 'outputter.py'], 
      stdin=subprocess.PIPE, 
      stdout=slave, stderr=slave, close_fds=True) 

    while process.poll() is None: 
     # Just FYI timeout is the last argument to select.select 
     rlist, wlist, xlist = select.select([master], [], []) 
     for f in rlist: 
      output = os.read(f, 1000) # This is used because it doesn't block 
      sys.stdout.write(output) 
      sys.stdout.flush() 
    print "**ALL COMPLETED**" 

if __name__ == '__main__': 
    main() 

यह उपप्रक्रिया कोड outputter.py है:

यह उपप्रक्रिया धावक runner.py है। अजीब यादृच्छिक भाग यादृच्छिक अंतराल पर डेटा आउटपुट करने वाले प्रोग्राम को अनुकरण करने के लिए हैं। यदि आप चाहें तो इसे हटा सकते हैं। यह कोई फर्क नहीं करना चाहिए:

import time 
import sys 
import random 

def main(): 
    lines = ['hello', 'there', 'what', 'are', 'you', 'doing'] 
    for line in lines: 
     sys.stdout.write(line + random.choice(['', '\n'])) 
     sys.stdout.flush() 
     time.sleep(random.choice([1,2,3,4,5])/20.0) 
    sys.stdout.write("\ndone\n") 
    sys.stdout.flush() 

if __name__ == '__main__': 
    main() 

किसी भी मदद के लिए आप सभी प्रदान कर सकते हैं के लिए धन्यवाद!

अतिरिक्त टिप्पणी

Pty प्रयोग किया जाता है, क्योंकि मैं यह सुनिश्चित करें कि stdout बफ़र नहीं है चाहता हूँ।

उत्तर

10

सबसे पहले, os.read आप क्या राज्य के लिए ब्लॉक, इसके विपरीत है। हालांकि, यह select के बाद ब्लॉक नहीं करता है। एक बंद फ़ाइल डिस्क्रिप्टर पर os.read भी एक खाली स्ट्रिंग देता है, जिसे आप जांचना चाहते हैं।

वास्तविक समस्या तथापि कि मास्टर डिवाइस वर्णनकर्ता, बंद कर दिया कभी नहीं है इस प्रकार अंतिम select एक है कि ब्लॉक कर देगा है। दुर्लभ दौड़ की स्थिति में, बाल प्रक्रिया select और process.poll() के बीच निकल गई है और आपका प्रोग्राम अच्छी तरह से निकलता है। ज्यादातर समय हमेशा के लिए चुनिंदा ब्लॉक।

आप के रूप में izhak सब नरक ढीला टूटता द्वारा प्रस्तावित संकेत हैंडलर स्थापित कर लेते हैं; जब भी एक बच्चे की प्रक्रिया समाप्त हो जाती है, सिग्नल हैंडलर चलाया जाता है। सिग्नल हैंडलर चलाने के बाद, उस थ्रेड में मूल सिस्टम कॉल जारी नहीं किया जा सकता है, ताकि सिस्कल इनवोकेशन nonzero errno लौटाता है, जो अक्सर अजगर में कुछ यादृच्छिक अपवाद फेंक दिया जाता है। अब, अगर कहीं और अपने कार्यक्रम में आप किसी भी अवरुद्ध सिस्टम कॉल कि कैसे ऐसे अपवादों को संभालने के लिए पता नहीं है के साथ कुछ पुस्तकालय का उपयोग करें, यदि आप एक बहुत बड़ी मुसीबत में हैं (उदाहरण के लिए किसी भी os.read कहीं भी अब के बाद भी एक सफल select, एक अपवाद फेंक कर सकते हैं) ।

कुछ मतदान के खिलाफ कहीं भी खाली यादृच्छिक अपवादों का वजन, मुझे नहीं लगता कि select पर समय-समय पर बुरा विचार नहीं लगता है। वैसे भी आपकी प्रक्रिया प्रणाली पर केवल एकमात्र (धीमी) मतदान प्रक्रिया होगी।

+0

शानदार स्पष्टीकरण के लिए धन्यवाद। मैंने सोचा, थोड़ी देर के बाद, यह शायद एक टाइमआउट सेट करने के लिए सबसे अच्छा होगा। मैंने इजाक के समाधान की कोशिश की लेकिन हाँ, मैंने ऐसा करने के बाद कुछ अजीब व्यवहार देखा। यह बहुत मदद करता है! – ravenac95

+0

अपने स्वयं के सुधार के लिए, क्या आप समझा सकते हैं कि मेरा जवाब कम क्यों हुआ? यह आपको किसी भी टाइमआउट का उपयोग करने से बचना चाहिए। –

+0

मैंने आपके सुझावों को [संबंधित प्रश्न के उत्तर] में लागू किया है (http://stackoverflow.com/a/12471855/4279) – jfs

0

जो मैं समझता हूं उससे आपको pty का उपयोग करने की आवश्यकता नहीं है। runner.py

import subprocess 
import sys 

def main(): 
     process = subprocess.Popen(['python', 'outputter.py'], 
         stdin=subprocess.PIPE, 
         stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

     while process.poll() is None: 
       output = process.stdout.readline() 
       sys.stdout.write(output) 
       sys.stdout.flush() 
     print "**ALL COMPLETED**" 

if __name__ == '__main__': 
     main() 

process.stdout.read(1) के रूप में संशोधित किया जा सकता उपप्रक्रिया से चरित्र प्रति वास्तविक समय उत्पादन के लिए process.stdout.readline() के स्थान पर किया जा सकता है।

नोट: यदि आपको उपप्रोसेस से रीयल-टाइम आउटपुट की आवश्यकता नहीं है, तो मतदान लूप से बचने के लिए Popen.communicate का उपयोग करें।

+0

घबराहट: प्रतिक्रिया के लिए धन्यवाद लेकिन मैं वास्तव में यह सुनिश्चित करना चाहता हूं कि कोई आउटपुट बफर नहीं किया गया है, इसलिए पीटीई की आवश्यकता है। मैं यह स्पष्ट करने के लिए प्रश्न संपादित करूंगा कि यह एक आवश्यकता है। – ravenac95

+0

यदि प्रोग्राम 'runner.py' के साथ बातचीत कर रहे हैं, तो पाइथन वाले हैं, तो आप unbuffered आउटपुट को सक्षम करने के लिए पॉपन कमांड में' python -u' जोड़ सकते हैं। मैंने 'outputter.py' के साथ परीक्षण किया और यह काम किया। दुर्भाग्य से – panickal

+0

, वे हमेशा अजगर अनुप्रयोग नहीं होंगे: -/ – ravenac95

0

जब आपका बच्चा प्रक्रिया निकलता है - आपकी मूल प्रक्रिया SIGCHLD संकेत प्राप्त करती है।डिफ़ॉल्ट रूप से यह संकेत नजरअंदाज कर दिया है, लेकिन आप इसे रोकने कर सकते हैं:

import sys 
import signal 

def handler(signum, frame): 
    print 'Child has exited!' 
    sys.exit(0) 

signal.signal(signal.SIGCHLD, handler) 

संकेत भी 'पढ़ा' 'चयन' या (या जो भी आप में हैं) और आप जो कुछ भी करना है यह बताने के लिए अवरुद्ध syscall तोड़ना चाहिए (सफाई, निकास, आदि) हैंडलर समारोह में।

8

आपके कोड को सही बनाने के लिए आप कई चीजें बदल सकते हैं। सबसे आसान बात यह है कि मैं सोच सकता हूं कि फोर्सिंग के बाद दास एफडी की अपनी मूल प्रक्रिया की प्रति को बंद करना है, ताकि जब बच्चा अपने दास एफडी से बाहर निकलता है और बंद कर देता है, तो माता-पिता का select.select() मास्टर को पढ़ने के लिए उपलब्ध के रूप में चिह्नित करेगा, और बाद में os.read() एक खाली परिणाम देगा और आपका कार्यक्रम पूरा हो जाएगा।

os.close(slave) 

तुरंत बाद ..placed:, केवल एक पंक्ति (Pty गुरु नहीं गुलाम बंद किया जा रहा है जब तक गुलाम fd की दोनों प्रतियां बंद हो जाती हैं के रूप में अंत में दिखाई देगा।)

तो subprocess.Popen कॉल, आपकी समस्या को ठीक करना चाहिए।

हालांकि, आपकी आवश्यकताओं के अनुसार, संभवतः बेहतर उत्तर हैं। जैसा कि किसी और ने नोट किया है, आपको बफरिंग से बचने के लिए केवल एक पीटीआई की आवश्यकता नहीं है। आप pty.openpty() के स्थान पर एक नंगे os.pipe() का उपयोग कर सकते हैं (और वापसी मूल्य का बिल्कुल वही व्यवहार करें)। एक नंगे ओएस पाइप कभी बफर नहीं होगा; अगर बाल प्रक्रिया अपने आउटपुट को बफर नहीं कर रही है, तो आपके select() और os.read() कॉल बफरिंग को नहीं देख पाएंगे। हालांकि, आपको अभी भी os.close(slave) लाइन की आवश्यकता होगी।

लेकिन यह संभव है कि आपको विभिन्न कारणों से एक पीटीई की आवश्यकता हो। यदि आपके कुछ बच्चे कार्यक्रम समय-समय पर पारस्परिक रूप से चलने की उम्मीद करते हैं, तो वे यह देखने के लिए जांच कर रहे हैं कि क्या उनका स्टडीन एक पीटीआई है और उत्तर के आधार पर अलग-अलग व्यवहार कर रहा है (बहुत सी सामान्य उपयोगिताएं ऐसा करती हैं)। यदि आप वास्तव में चाहते हैं कि बच्चे को लगता है कि उसके लिए आवंटित टर्मिनल है, तो pty मॉड्यूल जाने का तरीका है। कैसे आप runner.py चलाएँगे के आधार पर आप pty.py के लिए, pty.fork() को subprocess का उपयोग करने से स्विच करने के लिए इतना है कि बच्चे अपने सत्र आईडी सेट है और प्राइवेट पूर्व खोला (या देखने के स्रोत को देखने के लिए वह क्या करता है की आवश्यकता हो सकती है और अपने subprocess ऑब्जेक्ट के preexec_fn कॉल करने योग्य में उचित भागों को डुप्लिकेट करें)।

+0

वास्तव में, दास वर्णनकर्ता बंद नहीं किया गया था, और यह बुरा नहीं है। हालांकि, यह लाइन अभी तक पर्याप्त नहीं है, क्योंकि os.read त्रुटिपूर्ण = ईआईओ के साथ बाल प्रक्रिया की हत्या के प्रति प्रतिक्रिया करता है, इस प्रकार सभी पढ़ने को इरनो = ईआईओ और इसके पीछे कारण की जांच को छोड़कर कोशिश की जानी चाहिए। –

+0

हम्म, पाइप से पढ़ने पर ईआईओ प्राप्त करने का कोई कारण नहीं होना चाहिए। पढ़ने के पक्ष में, आपको बस पॉज़िक्स अर्थशास्त्र के तहत एक छोटा सा पढ़ना चाहिए (इसलिए इस मामले में, खाली स्ट्रिंग- पायथन ईओएफ)। –

+1

ठीक है, मैंने कोशिश की और मुझे लिनक्स 3.2 –