शेल कमांड चलाना और आउटपुट कैप्चर करना


905

मैं एक फ़ंक्शन लिखना चाहता हूं जो शेल कमांड को निष्पादित करेगा और इसके आउटपुट को एक स्ट्रिंग के रूप में लौटाएगा , कोई बात नहीं, यह एक त्रुटि या सफलता संदेश है। मैं बस यही परिणाम प्राप्त करना चाहता हूं कि मुझे कमांड लाइन के साथ मिल गया होगा।

एक कोड उदाहरण क्या होगा जो ऐसा काम करेगा?

उदाहरण के लिए:

def run_command(cmd):
    # ??????

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'

2
jfs

जवाबों:


1137

इस प्रश्न का उत्तर आपके द्वारा उपयोग किए जा रहे पायथन के संस्करण पर निर्भर करता है। subprocess.check_outputफ़ंक्शन का उपयोग करने के लिए सबसे सरल तरीका है :

>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

check_outputएकल प्रोग्राम चलाता है जो केवल तर्कों को इनपुट के रूप में लेता है। 1 यह परिणाम उसी तरह देता है जैसे कि मुद्रित किया जाता है stdout। यदि आपको इनपुट लिखने की आवश्यकता है stdin, तो आगे runया Popenवर्गों को छोड़ें । यदि आप जटिल शेल कमांड निष्पादित करना चाहते हैं, तो shell=Trueइस उत्तर के अंत में नोट देखें ।

check_outputसमारोह अभी भी व्यापक उपयोग (2.7+) में अजगर के लगभग सभी संस्करणों पर काम करता है। 2 लेकिन अधिक हाल के संस्करणों के लिए, यह अब अनुशंसित दृष्टिकोण नहीं है।

पायथन के आधुनिक संस्करण (3.5 या अधिक): run

यदि आप पायथन 3.5 या उच्चतर का उपयोग कर रहे हैं , और पीछे की संगतता की आवश्यकता नहीं है , तो नए runफ़ंक्शन की सिफारिश की जाती है। यह subprocessमॉड्यूल के लिए एक बहुत ही सामान्य, उच्च-स्तरीय एपीआई प्रदान करता है। किसी प्रोग्राम के आउटपुट को कैप्चर करने के लिए, subprocess.PIPEफ्लैग को stdoutकीवर्ड तर्क में पास करें। फिर stdoutलौटे CompletedProcessऑब्जेक्ट की विशेषता तक पहुँचें:

>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

वापसी मूल्य एक bytesवस्तु है, इसलिए यदि आप एक उचित स्ट्रिंग चाहते हैं, तो आपको इसकी आवश्यकता होगी decode। यूटीएफ -8-एन्कोडेड स्ट्रिंग नामक प्रक्रिया को वापस लेता है:

>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

यह सब एक-लाइनर के लिए संकुचित किया जा सकता है:

>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

यदि आप प्रक्रिया के लिए इनपुट पास करना चाहते हैं stdin, bytesतो inputकीवर्ड तर्क के लिए एक ऑब्जेक्ट पास करें :

>>> cmd = ['awk', 'length($0) > 5']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'

आप stderr=subprocess.PIPE(कैप्चर टू result.stderr) या stderr=subprocess.STDOUT( result.stdoutनियमित आउटपुट के साथ कैप्चर ) करके त्रुटियों को पकड़ सकते हैं । जब सुरक्षा चिंता का विषय नहीं है, तो आप shell=Trueनीचे दिए गए नोट्स में वर्णित करके अधिक जटिल शेल कमांड भी चला सकते हैं।

यह चीजों को करने के पुराने तरीके की तुलना में, बस थोड़ी जटिलता जोड़ता है। लेकिन मुझे लगता है कि यह भुगतान के लायक है: अब आप लगभग कुछ भी कर सकते हैं जो आपको runअकेले कार्य करने की आवश्यकता है ।

पायथन के पुराने संस्करण (2.7-3.4): check_output

यदि आप पायथन के पुराने संस्करण का उपयोग कर रहे हैं, या मामूली पीछे की संगतता की आवश्यकता है, तो आप संभवतः check_outputऊपर वर्णित संक्षेप में फ़ंक्शन का उपयोग कर सकते हैं। यह पायथन 2.7 के बाद से उपलब्ध है।

subprocess.check_output(*popenargs, **kwargs)  

यह उसी तर्क को लेता है जैसे Popen(नीचे देखें), और प्रोग्राम के आउटपुट में एक स्ट्रिंग देता है। इस उत्तर की शुरुआत का अधिक विस्तृत उपयोग उदाहरण है। पायथन 3.5 और उससे अधिक में, के साथ check_outputनिष्पादित करने के लिए बराबर है और , और केवल विशेषता को वापस करना ।runcheck=Truestdout=PIPEstdout

आप पास कर सकते हैं stderr=subprocess.STDOUTकि त्रुटि संदेश लौटे उत्पादन में शामिल हैं सुनिश्चित करने के लिए - लेकिन अजगर गुजर के कुछ संस्करणों में stderr=subprocess.PIPEकरने के लिए check_outputकर सकते हैं कारण गतिरोध । जब सुरक्षा चिंता का विषय नहीं है, तो आप shell=Trueनीचे दिए गए नोट्स में वर्णित करके अधिक जटिल शेल कमांड भी चला सकते हैं।

यदि आपको stderrप्रक्रिया से इनपुट या पास करने की आवश्यकता है, check_outputतो कार्य तक नहीं होगा। Popenउस मामले में नीचे दिए गए उदाहरण देखें ।

पायथन के जटिल अनुप्रयोग और विरासत संस्करण (2.6 और नीचे): Popen

यदि आपको गहरी बैकवर्ड संगतता की आवश्यकता है, या यदि आपको check_outputप्रदान की तुलना में अधिक परिष्कृत कार्यक्षमता की आवश्यकता है , तो आपको सीधे Popenऑब्जेक्ट्स के साथ काम करना होगा , जो कि सबप्रोसेस के लिए निम्न-स्तरीय एपीआई को एन्क्रिप्ट करता है।

Popenनिर्माता या तो स्वीकार करता है एक भी आदेश तर्क के बिना, या एक सूची अपनी पहली आइटम के रूप में एक कमांड, तर्क के किसी भी संख्या, सूची में एक अलग आइटम के रूप में प्रत्येक के बाद से युक्त। shlex.splitउचित रूप से स्वरूपित सूचियों में पार्स स्ट्रिंग्स की मदद कर सकते हैं। Popenऑब्जेक्ट भी प्रक्रिया IO प्रबंधन और निम्न-स्तरीय कॉन्फ़िगरेशन के लिए विभिन्न तर्कों के एक मेजबान को स्वीकार करते हैं ।

इनपुट और कैप्चर आउटपुट भेजने के लिए, communicateलगभग हमेशा पसंदीदा तरीका है। जैसे की:

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

या

>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo

यदि आप सेट करते हैं stdin=PIPE, तो आप communicateडेटा को प्रक्रिया से गुजरने की अनुमति भी देते हैं stdin:

>>> cmd = ['awk', 'length($0) > 5']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
...                           stderr=subprocess.PIPE,
...                           stdin=subprocess.PIPE)
>>> out, err = p.communicate('foo\nfoofoo\n')
>>> print out
foofoo

नोट हारून हॉल के जवाब है, जो कि इंगित करता है कुछ सिस्टम पर, आप सेट करना पड़ सकता है stdout, stderrऔर stdinसभी के लिए PIPE(या DEVNULL) प्राप्त करने के लिए communicateकाम करने के लिए सभी पर।

कुछ दुर्लभ मामलों में, आपको जटिल, वास्तविक समय आउटपुट कैप्चरिंग की आवश्यकता हो सकती है। वार्टेक का उत्तर आगे बढ़ने का एक तरीका बताता है, लेकिन communicateअगर ध्यान से इस्तेमाल न किया जाए तो इसके अलावा अन्य तरीके गतिरोध की आशंका रखते हैं।

उपरोक्त सभी कार्यों के साथ, जब सुरक्षा चिंता का विषय नहीं है, तो आप पास करके अधिक जटिल शेल कमांड चला सकते हैं shell=True

टिप्पणियाँ

1. रनिंग शेल कमांड: shell=Trueतर्क

सामान्य रूप से, प्रत्येक कॉल टू run, check_outputया Popenकंस्ट्रक्टर एकल प्रोग्राम को निष्पादित करता है । इसका मतलब है कि कोई फैंसी बैश स्टाइल पाइप नहीं है। यदि आप जटिल शेल कमांड चलाना चाहते हैं, तो आप पास कर सकते हैं shell=True, जो तीनों कार्यों का समर्थन करता है।

हालाँकि, ऐसा करना सुरक्षा चिंताओं को बढ़ाता है । यदि आप लाइट स्क्रिप्टिंग से अधिक कुछ भी कर रहे हैं, तो आप प्रत्येक प्रक्रिया को अलग से कॉल करने से बेहतर हो सकते हैं, और प्रत्येक से आउटपुट को अगले इनपुट के रूप में पास कर सकते हैं, के माध्यम से

run(cmd, [stdout=etc...], input=other_output)

या

Popen(cmd, [stdout=etc...]).communicate(other_output)

पाइपों को सीधे कनेक्ट करने का प्रलोभन मजबूत है; इसका प्रतिरोध करें। अन्यथा, आपको संभवत: गतिरोध दिखाई देगा या इस तरह की हैकिंग चीजें करनी होंगी ।

2. यूनिकोड विचार

check_outputपाइथन 2 में एक स्ट्रिंग देता है, लेकिन bytesपाइथन 3 में एक वस्तु। यह यूनिकोड के बारे में जानने के लिए एक क्षण लेने के लायक है यदि आपने पहले ही नहीं किया है।


5
दोनों के साथ check_output()और communicate()आपको प्रक्रिया पूरी होने तक इंतजार करना होगा, साथ ही poll()आपको आउटपुट भी मिलेगा। वास्तव में निर्भर करता है कि आपको क्या चाहिए।
vartec

2
सुनिश्चित नहीं है कि यह केवल पायथन के बाद के संस्करणों पर लागू होता है, लेकिन चर मेरे लिए outप्रकार <class 'bytes'>का था। एक स्ट्रिंग के रूप में आउटपुट प्राप्त करने के लिए मुझे इसे प्रिंट करने से पहले डीकोड करना होगा:out.decode("utf-8")
PolyMesh

1
जब आप पास होते हैं तो आपके लिए यह काम नहीं करता है shell=True? इससे मेरा काम बनता है। shlex.splitजब आप पास करते हैं तो आपको इसकी आवश्यकता नहीं होती है shell=Trueshlex.splitगैर-शेल कमांड के लिए है। मुझे लगता है कि मैं उस बिट को बाहर निकालने जा रहा हूं क्योंकि यह पानी को खराब कर रहा है।
12

2
पायथन 3.5+ एक कीवर्ड तर्क की अनुमति देता है universal_newlines=Trueजो आपको सिस्टम के डिफ़ॉल्ट एन्कोडिंग में यूनिकोड के तारों को पास करने और बाहर निकलने देता है। 3.7 में इसका नाम बदलकर अधिक समझदार कर दिया गया text=True
ट्रिपल जू

2
पायथन 3.6+ के लिए आप उपयोग करने encodingके subprocess.runबजाय पैरामीटर का उपयोग result.stdout.decode('utf-8')कर सकते हैं, आप उपयोग कर सकते हैं subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8')
पियरे

191

यह आसान है, लेकिन केवल यूनिक्स (साइगविन सहित) और पायथन 2.7 पर काम करता है।

import commands
print commands.getstatusoutput('wc -l file')

यह (रिटर्न_वल्यू, आउटपुट) के साथ एक टपल लौटाता है।

Python2 और Python3 दोनों में काम करने वाले समाधान के लिए, subprocessइसके बजाय मॉड्यूल का उपयोग करें :

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response


22
ध्यान दें कि यह यूनिक्स-विशिष्ट है। यह उदाहरण के लिए विंडोज पर विफल हो जाएगा।
ज़िट्रैक्स

4
+1 मुझे अजगर 2.4 के प्राचीन संस्करण पर काम करना है और यह बहुत मददगार था
javadba

1
PIPE दोस्त क्या है पूरा कोड दिखाने के लिए: subprocess.PIPE
Kyle Bridenstine

@KyleBridenstine आप उत्तर संपादित कर सकते हैं।
बोरिस

106

ऐसा कुछ:

def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while(True):
        # returns None while subprocess is running
        retcode = p.poll() 
        line = p.stdout.readline()
        yield line
        if retcode is not None:
            break

ध्यान दें, कि मैं stdout को stderr पर पुनर्निर्देशित कर रहा हूं, यह ठीक वैसा नहीं हो सकता जैसा आप चाहते हैं, लेकिन मुझे त्रुटि संदेश भी चाहिए।

यह फ़ंक्शन लाइन के अनुसार लाइन देता है क्योंकि वे आते हैं (आम तौर पर आपको सबप्रोसेस के लिए आउटपुट के रूप में समाप्त होने के लिए इंतजार करना होगा)।

आपके मामले के लिए उपयोग होगा:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,

संभावित गतिरोधों waitऔर callकार्यों से बचने के लिए आउटपुट प्राप्त करने के लिए किसी प्रकार के सक्रिय लूप को लागू करना सुनिश्चित करें ।
एंड्रे कारोन

@ सिल्वर लाइट: आपकी प्रक्रिया संभवतः उपयोगकर्ता से इनपुट की प्रतीक्षा कर रही है। रिटर्न के रूप में जल्द ही उस फ़ाइल के PIPEलिए एक मूल्य प्रदान करने stdinऔर बंद करने का प्रयास करें Popen
एंड्रे कारोन

4
-1: यह एक अनंत लूप है यदि retcodeहै 0। जाँच होनी चाहिए if retcode is not None। आपको खाली तारों का उत्पादन नहीं करना चाहिए (यहां तक ​​कि एक खाली लाइन कम से कम एक प्रतीक '\ n' है) if line: yield line:। p.stdout.close()आखिर में बुलाओ ।
jfs

2
मैंने ls -l / dirname के साथ कोड की कोशिश की और यह दो फाइलों को सूचीबद्ध करने के बाद टूट गया, जबकि निर्देशिका में बहुत अधिक फाइलें हैं
Vasilis

3
@fuenfundachtzig: सभी आउटपुट पढ़ने .readlines()तक वापस नहीं आएगा और इसलिए यह बड़े आउटपुट के लिए टूट जाता है जो मेमोरी में फिट नहीं होता है। if retcode is not None: yield from p.stdout.readlines(); break
उपप्रकार से

67

Vartec का उत्तर सभी पंक्तियों को नहीं पढ़ता है, इसलिए मैंने एक संस्करण बनाया जो किया:

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

उपयोग स्वीकृत उत्तर के समान है:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)

6
आप इस्तेमाल कर सकते हैं return iter(p.stdout.readline, b''), जबकि पाश के बजाय
JFS

2
यह पुनरावृति का एक अच्छा उपयोग है, यह नहीं पता था! मैंने कोड अपडेट किया।
मैक्स एकमन

मुझे पूरा यकीन है कि स्टडआउट सभी आउटपुट रखता है, यह एक बफर के साथ एक स्ट्रीम ऑब्जेक्ट है। मैं एक पूरी तकनीक का उपयोग करता हूं एक पोपेन पूरा होने के बाद शेष सभी आउटपुट को समाप्त करने के लिए, और मेरे मामले में, पोल () और रीडलाइन का उपयोग करके आउटपुट लाइव कैप्चर करने के लिए निष्पादन के दौरान।
मैक्स एकमैन

मैंने अपनी भ्रामक टिप्पणी को हटा दिया है। मैं पुष्टि कर सकता हूं, p.stdout.readline()भले ही बच्चा प्रक्रिया पहले से ही बाहर हो गई है ( p.poll()नहीं है None) गैर-खाली पहले-बफ़र किए गए आउटपुट को वापस कर सकता है ।
जून १12

यह कोड काम नहीं करता है। यहाँ देखें stackoverflow.com/questions/24340877/...
थांग

61

यह एक मुश्किल लेकिन सुपर सरल उपाय है जो कई स्थितियों में काम करता है:

import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()

एक अस्थायी फ़ाइल (यहां tmp है) कमांड के आउटपुट के साथ बनाई गई है और आप इसे अपने वांछित आउटपुट से पढ़ सकते हैं।

टिप्पणियों से अतिरिक्त ध्यान दें: आप एक बार की नौकरी के मामले में tmp फ़ाइल को निकाल सकते हैं। यदि आपको कई बार ऐसा करने की आवश्यकता है, तो tmp को हटाने की कोई आवश्यकता नहीं है।

os.remove('tmp')

5
हैकी लेकिन सुपर सरल + कहीं भी काम करता है .. इसे संयोजित कर सकते हैं mktempताकि यह थ्रेडेड स्थितियों में काम कर सके, मुझे लगता है
प्रकाश राजगोपाल

2
हो सकता है कि सबसे तेज़ तरीका हो, लेकिन os.remove('tmp')इसे "फिल्महीन" बनाने के लिए बेहतर है ।
जूमुके जूल

@XuMuK आप एक समय की नौकरी के मामले में सही हैं। यदि यह दोहराव वाला काम है तो शायद हटाना जरूरी नहीं है
मेहदी समन बोय


1
@ 2mia स्पष्ट रूप से यह एक कारण के लिए आसान है! यदि आप समवर्ती पठन और लेखन के लिए फ़ाइल को एक प्रकार की साझा मेमोरी के रूप में उपयोग करना चाहते हैं, तो यह एक अच्छा विकल्प नहीं है। लेकिन, s.th के लिए। जैसे कमांड का आउटपुट होना (जैसे ls या ढूंढना या ...) यह एक अच्छा और तेज़ विकल्प हो सकता है। Btw अगर आप एक सरल समस्या के लिए एक तेजी से समाधान की जरूरत है यह सबसे अच्छा मुझे लगता है। यदि आपको एक पाइपलाइन की आवश्यकता है, तो उप -प्रकार आपके लिए अधिक कुशल है।
मेहदी समन बोय

44

मैं एक ही समस्या थी, लेकिन यह करने का एक बहुत ही सरल तरीका समझ में आया:

import subprocess
output = subprocess.getoutput("ls -l")
print(output)

आशा है कि यह मदद करता है

नोट: यह समाधान Python3 विशिष्ट है क्योंकि Python2 में subprocess.getoutput()काम नहीं करता है


यह ओपी की समस्या को कैसे हल करता है? कृपया विस्तार से बताएं।
रामेंखेफ

4
यह स्ट्रिंग के रूप में कमांड का आउटपुट लौटाता है, जैसा कि सरल है
azhar22k

1
बेशक, प्रिंट अजगर 2 पर एक बयान है। आपको यह पता लगाने में सक्षम होना चाहिए कि यह पायथन 3 का उत्तर है।

2
@ डेव प्रिंट (s) वैध अजगर है। सबप्रोसेस.गेटआउट नहीं है।
user48956

2
अधिकांश उपयोग के मामलों के लिए, यह वही है जो लोग संभवतः चाहते हैं: याद रखना आसान है, परिणाम को डिकोड नहीं करना है, आदि। धन्यवाद।
bwv549

18

आप किसी भी शेल कमांड को चलाने के लिए निम्न कमांड का उपयोग कर सकते हैं। मैं उन्हें ubuntu पर इस्तेमाल किया है।

import os
os.popen('your command here').read()

नोट: यह अजगर 2.6 के बाद से हटा दिया गया है। अब आप अवश्य उपयोग करें subprocess.Popen। नीचे उदाहरण है

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")

2
के बाद से खारिज किया गया संस्करण 2.6 - docs.python.org/2/library/os.html#os.popen
फिलिपो Vitale

1
@FilippoVitale साभार मुझे नहीं पता था कि यह पदावनत है।
मुहम्मद हसन

1
Raspberrypi.stackexchange.com/questions/71547/… के अनुसार os.popen(), Python 2.6 में पदावनत किया गया है, लेकिन यह Python 3.x में पदावनत नहीं है , क्योंकि 3.x में इसका उपयोग करके लागू किया गया है subprocess.Popen()
JL

12

आपका माइलेज मई वैरी, मैंने पायथन 2.6.5 पर विंडोज में वर्टेक के समाधान पर @ प्रेषक की स्पिन का प्रयास किया, लेकिन मुझे त्रुटियां हो रही थीं, और किसी अन्य समाधान ने काम नहीं किया। मेरी त्रुटि थी WindowsError: [Error 6] The handle is invalid:।

मैंने पाया कि मुझे अपनी पसंद का आउटपुट प्राप्त करने के लिए PIPE को हर हैंडल को असाइन करना था - मेरे लिए निम्न काम किया।

import subprocess

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    return subprocess.Popen(cmd, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE).communicate()

और इस तरह से कॉल करें, ( [0]टपल का पहला तत्व प्राप्त करता है stdout):

run_command('tracert 11.1.0.1')[0]

अधिक सीखने के बाद, मुझे विश्वास है कि मुझे इन पाइप तर्कों की आवश्यकता है क्योंकि मैं एक कस्टम सिस्टम पर काम कर रहा हूं जो विभिन्न हैंडल का उपयोग करता है, इसलिए मुझे सीधे सभी एसटीडी को नियंत्रित करना था।

कंसोल पॉपअप (विंडोज के साथ) को रोकने के लिए, यह करें:

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    # instantiate a startupinfo obj:
    startupinfo = subprocess.STARTUPINFO()
    # set the use show window flag, might make conditional on being in Windows:
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    # pass as the startupinfo keyword argument:
    return subprocess.Popen(cmd,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            startupinfo=startupinfo).communicate()

run_command('tracert 11.1.0.1')

1
रुचिकर - यह एक विंडोज चीज होनी चाहिए। मैं इस मामले में लोगों को इसी तरह की त्रुटियों के लिए इशारा करते हुए एक नोट जोड़ूंगा।
प्रेषित

का उपयोग DEVNULLकरने के बजाय की subprocess.PIPEअगर आप कुछ भी न लिखें / एक पाइप से पढ़ने अन्यथा आप बच्चे प्रक्रिया लटका कर सकते हैं।
10

10

मैं निम्नलिखित आवश्यकताओं के साथ एक ही समस्या का थोड़ा अलग स्वाद था:

  1. कैप्चर करें और STDOUT संदेशों को वापस करें क्योंकि वे STDOUT बफर में जमा होते हैं (यानी रियलटाइम में)।
    • @vartec ने अपने जनरेटरों के उपयोग और
      उपरोक्त 'उपज' कीवर्ड के साथ इस Pythonically को हल किया
  2. सभी STDOUT लाइनों को प्रिंट करें ( भले ही STDOUT बफर से पहले प्रक्रिया पूरी तरह से पढ़ी जा सके )
  3. उच्च-आवृत्ति पर प्रक्रिया को पोप करते हुए सीपीयू चक्र बर्बाद न करें
  4. उपप्रकार की वापसी कोड की जाँच करें
  5. यदि हमें नॉन-जीरो एरर रिटर्न कोड मिलता है तो STDERR (STDOUT से अलग) प्रिंट करें।

मैंने निम्नलिखित के साथ आने के लिए पिछले उत्तरों को संयुक्त और ट्विक किया है:

import subprocess
from time import sleep

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         shell=True)
    # Read stdout from subprocess until the buffer is empty !
    for line in iter(p.stdout.readline, b''):
        if line: # Don't print blank lines
            yield line
    # This ensures the process has completed, AND sets the 'returncode' attr
    while p.poll() is None:                                                                                                                                        
        sleep(.1) #Don't waste CPU-cycles
    # Empty STDERR buffer
    err = p.stderr.read()
    if p.returncode != 0:
       # The run_command() function is responsible for logging STDERR 
       print("Error: " + str(err))

इस कोड को पिछले उत्तरों के समान ही निष्पादित किया जाएगा:

for line in run_command(cmd):
    print(line)

1
क्या आप बताते हैं कि नींद के अतिरिक्त (.1) सीपीयू चक्रों को कैसे बर्बाद नहीं किया जाएगा?
मोताज़ एल्माश्री

2
यदि हम p.poll()कॉल के बीच में किसी भी नींद के बिना कॉल करना जारी रखते हैं, तो हम इस फ़ंक्शन को लाखों बार कॉल करके सीपीयू चक्रों को बर्बाद कर देंगे। इसके बजाय, हम ओएस को बताकर हमारे पाश को "थ्रॉटल" करते हैं कि हमें अगले 1/10 वें सेकंड के लिए परेशान होने की आवश्यकता नहीं है, इसलिए यह अन्य कार्यों को पूरा कर सकता है। (यह संभव है कि p.poll () भी सोता है, जिससे हमारी नींद का विवरण बेमानी हो जाता है)।
ऐल्फिन

5

के लिए प्रारंभिक कमांड को विभाजित करना subprocess मुश्किल और बोझिल हो सकता है।

उपयोग shlex.split()अपने आप को मदद करने के लिए ।

नमूना आदेश

git log -n 5 --since "5 years ago" --until "2 year ago"

कोड

from subprocess import check_output
from shlex import split

res = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

shlex.split()कोड के बिना निम्नानुसार दिखेगा

res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    '5 years ago', 
    '--until', 
    '2 year ago'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

1
shlex.split()एक सुविधा है, खासकर यदि आप नहीं जानते कि शेल कैसे काम करता है; लेकिन मैन्युअल रूप से इस स्ट्रिंग को सूची ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']में परिवर्तित करना कठिन नहीं है यदि आप उद्धरण को समझते हैं।
ट्रिपल

4

यदि आपको कई फाइलों पर शेल कमांड चलाने की आवश्यकता है, तो इसने मेरे लिए चाल चली।

import os
import subprocess

# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec's solution because it wasn't printing all lines)
def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

# Get all filenames in working directory
for filename in os.listdir('./'):
    # This command will be run on each file
    cmd = 'nm ' + filename

    # Run the command and capture the output line by line.
    for line in runProcess(cmd.split()):
        # Eliminate leading and trailing whitespace
        line.strip()
        # Split the output 
        output = line.split()

        # Filter the output and print relevant lines
        if len(output) > 2:
            if ((output[2] == 'set_program_name')):
                print filename
                print line

संपादित करें: सिर्फ जेएफ सेबेस्टियन के सुझाव के साथ मैक्स पर्सन के समाधान को देखा। आगे बढ़े और उसे शामिल किया।


Popenया तो एक स्ट्रिंग को स्वीकार करता है, लेकिन फिर आपको ज़रूरत है shell=True, या तर्कों की एक सूची है, जिसमें आपको ['nm', filename]एक स्ट्रिंग के बजाय मामले में पास होना चाहिए । उत्तरार्द्ध बेहतर है क्योंकि शेल यहां कोई मूल्य प्रदान किए बिना जटिलता जोड़ता है। shell=Trueस्पष्ट रूप से बिना स्ट्रिंग पास करना विंडोज पर काम करने के लिए होता है, लेकिन यह किसी भी अगले पायथन संस्करण में बदल सकता है।
ट्रिपल जू

2

@Senderle के अनुसार, यदि आप मेरी तरह python3.6 का उपयोग करते हैं:

def sh(cmd, input=""):
    rst = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))
    assert rst.returncode == 0, rst.stderr.decode("utf-8")
    return rst.stdout.decode("utf-8")
sh("ls -a")

ठीक उसी तरह काम करेंगे जैसे आप कमांड को बैश में चलाते हैं


आप कीवर्ड तर्कों को पुनर्निर्मित कर रहे हैं check=True, universal_newlines=True। दूसरे शब्दों में, subprocess.run()आपका कोड पहले से ही सब कुछ करता है।
ट्रिपल जू

1

यदि आप subprocessअजगर मॉड्यूल का उपयोग करते हैं, तो आप अलग से STDOUT, STDERR और रिटर्न कोड ऑफ कमांड को संभालने में सक्षम हैं। आप संपूर्ण कमांड कॉलर कार्यान्वयन के लिए एक उदाहरण देख सकते हैं। बेशक आप इसे बढ़ा सकते हैंtry..exceptयदि आप चाहें तो हैं।

नीचे दिया गया फ़ंक्शन STDOUT, STDERR और रिटर्न कोड देता है ताकि आप उन्हें अन्य स्क्रिप्ट में संभाल सकें।

import subprocess

def command_caller(command=None)
    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False)
    out, err = sp.communicate()
    if sp.returncode:
        print(
            "Return code: %(ret_code)s Error message: %(err_msg)s"
            % {"ret_code": sp.returncode, "err_msg": err}
            )
    return sp.returncode, out, err

का एक और खराब कार्यान्वयन subprocess.run()। पहिया को सुदृढ़ मत करो।
ट्रिपल जू

0

उदाहरण के लिए, निष्पादित ('ls -ahl') विभेदित तीन / चार संभावित रिटर्न और ओएस प्लेटफॉर्म:

  1. कोई आउटपुट नहीं, लेकिन सफलतापूर्वक चलाएं
  2. उत्पादन खाली लाइन, सफलतापूर्वक चलाएँ
  3. रन विफल रहा
  4. आउटपुट कुछ, सफलतापूर्वक चलाएं

नीचे कार्य करें

def execute(cmd, output=True, DEBUG_MODE=False):
"""Executes a bash command.
(cmd, output=True)
output: whether print shell output to screen, only affects screen display, does not affect returned values
return: ...regardless of output=True/False...
        returns shell output as a list with each elment is a line of string (whitespace stripped both sides) from output
        could be 
        [], ie, len()=0 --> no output;    
        [''] --> output empty line;     
        None --> error occured, see below

        if error ocurs, returns None (ie, is None), print out the error message to screen
"""
if not DEBUG_MODE:
    print "Command: " + cmd

    # https://stackoverflow.com/a/40139101/2292993
    def _execute_cmd(cmd):
        if os.name == 'nt' or platform.system() == 'Windows':
            # set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        else:
            # Use bash; the default is sh
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable="/bin/bash")

        # the Popen() instance starts running once instantiated (??)
        # additionally, communicate(), or poll() and wait process to terminate
        # communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as tuple
        # if communicate(), the results are buffered in memory

        # Read stdout from subprocess until the buffer is empty !
        # if error occurs, the stdout is '', which means the below loop is essentially skipped
        # A prefix of 'b' or 'B' is ignored in Python 2; 
        # it indicates that the literal should become a bytes literal in Python 3 
        # (e.g. when code is automatically converted with 2to3).
        # return iter(p.stdout.readline, b'')
        for line in iter(p.stdout.readline, b''):
            # # Windows has \r\n, Unix has \n, Old mac has \r
            # if line not in ['','\n','\r','\r\n']: # Don't print blank lines
                yield line
        while p.poll() is None:                                                                                                                                        
            sleep(.1) #Don't waste CPU-cycles
        # Empty STDERR buffer
        err = p.stderr.read()
        if p.returncode != 0:
            # responsible for logging STDERR 
            print("Error: " + str(err))
            yield None

    out = []
    for line in _execute_cmd(cmd):
        # error did not occur earlier
        if line is not None:
            # trailing comma to avoid a newline (by print itself) being printed
            if output: print line,
            out.append(line.strip())
        else:
            # error occured earlier
            out = None
    return out
else:
    print "Simulation! The command is " + cmd
    print ""

0

आउटपुट को किसी टेक्स्ट फ़ाइल पर रीडायरेक्ट किया जा सकता है और फिर उसे वापस पढ़ा जा सकता है।

import subprocess
import os
import tempfile

def execute_to_file(command):
    """
    This function execute the command
    and pass its output to a tempfile then read it back
    It is usefull for process that deploy child process
    """
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    path = temp_file.name
    command = command + " > " + path
    proc = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if proc.stderr:
        # if command failed return
        os.unlink(path)
        return
    with open(path, 'r') as f:
        data = f.read()
    os.unlink(path)
    return data

if __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))

निश्चित रूप से यह हो सकता है, लेकिन आप क्यों करना चाहते हैं; और आप पास होने के बजाय शेल का उपयोग क्यों करेंगे stdout=temp_file?
ट्रिपल जू

दरअसल, सामान्य तरीके से आप सही हैं, लेकिन मेरे उदाहरण में ecls.exeएक और कमांड लाइन टूल को तैनात करना प्रतीत होता है, इसलिए सरल तरीका कभी-कभी काम नहीं करता है।
एमआर

0

बस कर्ल का उपयोग करके ऐसा करने के लिए एक छोटी सी बैश स्क्रिप्ट लिखी

https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5

#!/usr/bin/env bash

# Usage: gdrive_dl.sh <url>

urlBase='https://drive.google.com'
fCookie=tmpcookies

curl="curl -L -b $fCookie -c $fCookie"
confirm(){
    $curl "$1" | grep jfk-button-action | sed -e 's/.*jfk-button-action" href="\(\S*\)".*/\1/' -e 's/\&amp;/\&/g'
}

$curl -O -J "${urlBase}$(confirm $1)"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.