कार्यकारी सारांश (या "tl; डॉ।" संस्करण): यह आसान है जब वहाँ सबसे अधिक है subprocess.PIPE, अन्यथा यह कठिन है।
यह इस बारे में थोड़ा समझाने का समय हो सकता है कि subprocess.Popenयह कैसे काम करता है।
(कैविएट: यह पाइथन 2.x के लिए है, हालाँकि 3.x समान है; और मैं विंडोज वेरिएंट पर काफी फजी हूं। मुझे पॉसिक्स का सामान ज्यादा बेहतर लगता है।)
Popenसमारोह शून्य करने वाली तीन आई / ओ धाराओं, कुछ हद तक एक साथ के साथ सौदा करने की जरूरत है। ये चिह्नित हैं stdin, stdoutऔरstderr हमेशा की तरह।
आप प्रदान कर सकते हैं:
None, यह दर्शाता है कि आप स्ट्रीम को पुनर्निर्देशित नहीं करना चाहते हैं। यह इनकी बजाय हमेशा की तरह इनहेरिट करेगा। ध्यान दें कि POSIX सिस्टम पर, कम से कम, इसका मतलब यह नहीं है कि यह पायथन का उपयोग करेगा sys.stdout, बस पायथन का वास्तविक स्टडआउट; अंत में डेमो देखें।
- एक
intमान। यह एक "कच्ची" फ़ाइल डिस्क्रिप्टर (POSIX में कम से कम) है। (साइड नोट: PIPEऔर STDOUTवास्तव में intआंतरिक रूप से हैं, लेकिन "असंभव" वर्णनकर्ता, -1 और -2 हैं।)
- एक धारा - वास्तव में, एक
filenoविधि के साथ कोई भी वस्तु । Popenउस स्ट्रीम के लिए डिस्क्रिप्टर ढूंढेगा, उपयोग कर सकता है stream.fileno(), और फिर intमान के लिए आगे बढ़ सकता है ।
subprocess.PIPE, यह दर्शाता है कि पायथन को एक पाइप बनाना चाहिए।
subprocess.STDOUT( stderrकेवल के लिए): पायथन को उसी विवरणक के रूप में उपयोग करने के लिए कहें stdout। यह केवल तभी समझ में आता है जब आपने (गैर None) मूल्य प्रदान किया हो stdout, और तब भी, यदि आप सेट करते हैं , तो इसकी आवश्यकता हैstdout=subprocess.PIPE । (अन्यथा आप केवल वही तर्क प्रदान कर सकते हैं, जो आप के लिए प्रदान करते हैं stdout, उदाहरण के लिए Popen(..., stdout=stream, stderr=stream)।)
सबसे आसान मामले (कोई पाइप नहीं)
यदि आप कुछ भी नहीं अनुप्रेषित करते हैं (सभी तीनों को डिफ़ॉल्ट Noneमान या आपूर्ति को स्पष्ट छोड़ दें None), तो Pipeयह काफी आसान है। यह सिर्फ उपप्रजाति को स्पिन करने और इसे चलाने की जरूरत है। या, यदि आप एक गैर- या एक धारा के लिए पुनर्निर्देशित करते हैं PIPE- यह अभी भी आसान है, क्योंकि OS सभी काम करता है। पायथन को उप-प्रजाति को बंद करने की जरूरत है, जो उसके स्टड, स्टडआउट और / या स्टेंडर को उपलब्ध फाइल डिस्क्रिप्टर से जोड़ता है।intfileno()
अभी भी आसान मामला: एक पाइप
यदि आप केवल एक धारा को पुनर्निर्देशित करते हैं, तब Pipeभी चीजें बहुत आसान हैं। चलो एक समय में एक स्ट्रीम चुनें और देखें।
आप कुछ की आपूर्ति करना चाहते हैं मान लीजिए stdin, लेकिन जाने stdoutऔर stderrअन-निर्देशित कर दिये जाते हैं, या एक फ़ाइल वर्णनकर्ता में जाते हैं। मूल प्रक्रिया के रूप में, आपके पायथन कार्यक्रम को बस write()पाइप के नीचे डेटा भेजने के लिए उपयोग करने की आवश्यकता होती है । आप इसे स्वयं कर सकते हैं, जैसे:
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
proc.stdin.write('here, have some data\n') # etc
या आप स्टड डेटा को पास कर सकते हैं proc.communicate(), जो तब stdin.writeऊपर दिखाया गया है। कोई आउटपुट नहीं आ रहा है इसलिए communicate()केवल एक और वास्तविक काम है: यह आपके लिए पाइप को भी बंद कर देता है। (यदि आप कॉल नहीं करते हैं तो proc.communicate()आपको proc.stdin.close()पाइप को बंद करने के लिए कॉल करना होगा , ताकि उपप्रोसेस को पता चल सके कि कोई अधिक डेटा नहीं है।)
माना कि आप कब्जा करना चाहते हैं stdoutलेकिन छोड़ दें stdinऔर stderrअकेले। फिर से, यह आसान है: बस कॉल proc.stdout.read()(या समतुल्य) जब तक कि अधिक आउटपुट न हो। चूंकि proc.stdout()एक सामान्य अजगर I / O धारा है, आप इस पर सभी सामान्य निर्माणों का उपयोग कर सकते हैं, जैसे:
for line in proc.stdout:
या, फिर से, आप उपयोग कर सकते हैं proc.communicate(), जो बस read()आपके लिए करता है ।
यदि आप केवल कब्जा करना चाहते हैं stderr, तो यह उसी के साथ काम करता है stdout।
इससे पहले कि मुश्किल हो, एक और तरकीब है। मान लें कि आप कब्जा करना चाहते हैं stdout, और कब्जा भी करते हैं stderrलेकिन stdout के समान पाइप पर:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
इस मामले में, subprocess"धोखा"! ठीक है, इसे ऐसा करना है, इसलिए यह वास्तव में धोखा नहीं है: यह उपप्रकार को अपने स्टडआउट और इसके स्टडर के साथ (एकल) पाइप-डिस्क्रिप्टर में निर्देशित करता है जो इसके मूल (पायथन) प्रक्रिया को वापस फीड करता है। पैरेंट की ओर, आउटपुट को पढ़ने के लिए केवल एक ही पाइप-डिस्क्रिप्टर है। सभी "stderr" आउटपुट में दिखाई देता है proc.stdout, और यदि आप कॉल करते हैं proc.communicate(), तो stderr परिणाम (टुपल में दूसरा मूल्य) होगा None, न कि एक स्ट्रिंग।
कठिन मामले: दो या अधिक पाइप
जब आप कम से कम दो पाइप का उपयोग करना चाहते हैं तो सभी समस्याएं आती हैं। वास्तव में, subprocessकोड में ही यह बिट है:
def communicate(self, input=None):
...
# Optimization: If we are only using one pipe, or no pipe at
# all, using select() or threads is unnecessary.
if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
लेकिन, अफसोस, यहाँ हमने कम से कम दो, और शायद तीन, अलग-अलग पाइप बनाए हैं, इसलिए count(None)रिटर्न या तो 1 या 0. हमें चीजों को कठिन तरीके से करना चाहिए।
विंडोज पर, यह threading.Threadपरिणाम के लिए self.stdoutऔर जमा करने के लिए उपयोग करता है self.stderr, और इसमें पैरेंट थ्रेड डिलीवर self.stdinइनपुट डेटा होता है (और फिर पाइप को बंद करें)।
POSIX पर, यह pollउपलब्ध का उपयोग करता है, अन्यथा select, आउटपुट को संचित करने और स्टडिन इनपुट देने के लिए। यह सब (एकल) मूल प्रक्रिया / धागे में चलता है।
गतिरोध से बचने के लिए थ्रेड्स या पोल / सेलेक्ट की आवश्यकता है। उदाहरण के लिए, मान लीजिए कि हमने सभी तीन धाराओं को तीन अलग-अलग पाइपों पर पुनर्निर्देशित किया है। मान लीजिए कि लेखन प्रक्रिया निलंबित होने से पहले पाइप में कितना डेटा भरा जा सकता है, इसकी एक छोटी सी सीमा है, दूसरे छोर से पाइप को "साफ" करने के लिए पढ़ने की प्रक्रिया की प्रतीक्षा कर रहा है। आइए चित्रण के लिए उस छोटी सी सीमा को एक बाइट पर सेट करें। (यह वास्तव में है कि चीजें कैसे काम करती हैं, सिवाय इसके कि सीमा एक बाइट से बहुत बड़ी है।)
माता-पिता (अजगर) प्रक्रिया की कोशिश करता कई बाइट्स-कहते हैं, लिखने के लिए तो 'go\n'करने के लिए proc.stdin, पहली बाइट में चला जाता है और फिर दूसरी अजगर प्रक्रिया का कारण बनता है को निलंबित करने, उपप्रक्रिया के लिए इंतज़ार कर पहले बाइट को पढ़ने के लिए, पाइप खाली।
इस बीच, मान लीजिए कि उपप्रकार एक दोस्ताना "हैलो! डोंट पैनिक" प्रिंट करने का फैसला करता है। शुभकामना। Hइसके stdout पाइप में चला जाता है, लेकिन eनिलंबित करने के लिए यह कारण बनता है, इसके जनक के लिए है कि पढ़ने के लिए इंतजार करH , stdout पाइप खाली।
अब हम फंस गए हैं: पायथन प्रक्रिया सो रही है, "जाओ" कहकर समाप्त करने की प्रतीक्षा कर रहा है, और उपप्रकार भी सो रहा है, "हैलो! डोंट पैनिक!" कह कर समाप्त होने की प्रतीक्षा कर रहा है।
subprocess.Popenकोड सूत्रण या चयन / चुनाव के साथ इस समस्या से बचा जाता है। जब बाइट्स पाइप के ऊपर जा सकते हैं, तो वे जाते हैं। जब वे नहीं कर सकते हैं, तो केवल एक थ्रेड (पूरी प्रक्रिया नहीं) को सोना है - या, चयन / सर्वेक्षण के मामले में, पायथन प्रक्रिया एक साथ "प्रतीक्षा" या "डेटा उपलब्ध" के लिए इंतजार कर सकती है, प्रक्रिया की गति को लिखती है। केवल जब वहाँ कमरा है, और इसके स्टडआउट और / या stderr को केवल तभी पढ़ता है जब डेटा तैयार होता है। proc.communicate()कोड (वास्तव में _communicate, जहां बालों मामलों नियंत्रित किया जाता है) रिटर्न सब stdin डेटा एक बार (यदि हो तो) भेज दिया गया है और सभी stdout और / या stderr डेटा जमा किया गया है।
यदि आप दोनों stdoutऔर stderrदो अलग-अलग पाइपों (किसी भी stdinपुनर्निर्देशन की परवाह किए बिना ) को पढ़ना चाहते हैं , तो आपको गतिरोध से भी बचने की आवश्यकता होगी। यहां डेडलॉक परिदृश्य अलग है- यह तब होता है जब उपप्रोसेस कुछ लंबा लिखता है stderrजब आप डेटा खींच रहे होते हैं stdout, या इसके विपरीत - लेकिन यह अभी भी है।
डेमो
मैंने यह प्रदर्शित करने का वादा किया है कि, संयुक्त राष्ट्र पुनर्निर्देशित, पायथन subprocessएश अंतर्निहित स्टडआउट को लिखता है, नहीं sys.stdout। तो, यहाँ कुछ कोड है:
from cStringIO import StringIO
import os
import subprocess
import sys
def show1():
print 'start show1'
save = sys.stdout
sys.stdout = StringIO()
print 'sys.stdout being buffered'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
in_stdout = sys.stdout.getvalue()
sys.stdout = save
print 'in buffer:', in_stdout
def show2():
print 'start show2'
save = sys.stdout
sys.stdout = open(os.devnull, 'w')
print 'after redirect sys.stdout'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
sys.stdout = save
show1()
show2()
जब चला:
$ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
ध्यान दें कि पहले दिनचर्या यदि आप जोड़ने के असफल हो जायेगी stdout=sys.stdout, के रूप में एक StringIOवस्तु नहीं है fileno। दूसरा छोड़ जाएगा helloअगर आप को जोड़ने stdout=sys.stdoutके बाद से sys.stdoutपर पुनः निर्देशित कर दिया गया है os.devnull।
(यदि आप पायथन के फ़ाइल-डिस्क्रिप्टर -1 को पुनर्निर्देशित करते हैं , तो उपप्रकार उस पुनर्निर्देशन का पालन करेगा । open(os.devnull, 'w')कॉल एक स्ट्रीम उत्पन्न करता है, जो fileno()2. से अधिक है।)
Popen.pollरूप में उपयोग कर सकते हैं ।