पायथन में बैश कमांड चला रहा है


299

अपनी स्थानीय मशीन पर, मैं एक अजगर स्क्रिप्ट चलाता हूं, जिसमें यह रेखा है

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)

यह ठीक काम करता है।

फिर मैं एक सर्वर पर एक ही कोड चलाता हूं और मुझे निम्न त्रुटि संदेश मिलता है

'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import  diag
ImportError: No module named swap

तो मैंने जो किया, क्या वह डाला गया है print bashCommandजो टर्मिनल में कमांड की तुलना में मुझे प्रिंट करता है इससे पहले कि वह इसे चलाता है os.system()

बेशक, मुझे फिर से त्रुटि मिलती है (जिसके कारण os.system(bashCommand)) लेकिन उस त्रुटि से पहले यह टर्मिनल में कमांड प्रिंट करता है। तब मैंने बस उस आउटपुट को कॉपी किया और टर्मिनल में कॉपी पेस्ट किया और एंट्री मारा और यह काम किया ...

क्या किसी के पास कोई सुराग है कि क्या चल रहा है?


2
आपके द्वारा चलाए जाने के आधार पर वातावरण में अंतर प्रतीत होता है cwm। हो सकता है .bashrcकि आपके अंदर कुछ विन्यास हो जो इंटरएक्टिव बैश उपयोग के लिए वातावरण तैयार करता है?
स्वेन मार्नाच

क्या आपने सर्वर पर लॉग इन करते समय कमांड लाइन से कमांड चलाने की कोशिश की थी? आपकी पोस्ट बस आपको "टर्मिनल में [इसे] चिपकाया" कहती है।
स्वेन मार्नाच

@ स्वेन: हाँ, मेरा मतलब है कि मैंने कमांड को सीधे सर्वर के टर्मिनल में
चलाया

आप कैसे चलते हैं इसके आधार पर PYTHONPATH में अंतर प्रतीत होता है cwm। या हो सकता है कि PATH में अंतर है, और अलग-अलग संस्करण cwmकहलाते हैं। या अजगर के विभिन्न संस्करण। मशीन तक पहुंच के बिना यह पता लगाना बहुत मुश्किल है ...
स्वेन मार्नाच

जवाबों:


314

उपयोग न करें os.system। इसे उपप्रकार के पक्ष में हटा दिया गया है । से डॉक्स : "इस मॉड्यूल कई बड़े मॉड्यूल और कार्यों की जगह का इरादा रखता है: os.system, os.spawn"।

जैसे आपके मामले में:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()

8
यह वह नहीं था जो मैं चाहता था जब मुझे एक cd 'path\to\somewhere'और बैश कमांड का पालन करने की आवश्यकता थी जो कि कहीं न कहीं चलाने की आवश्यकता थी। @ user225312
एडविवि

36
@AvrightIV यदि आपको किसी विशेष कार्य निर्देशिका में चलाने के लिए अपने उपप्रकार की आवश्यकता है, तो आप cwdपोपेन के तर्क का उपयोग कर सकते हैं :subprocess.Popen(..., cwd='path\to\somewhere')
निविड़ अंधकार

7
मेरे आदेश के लिए मुझे शेल की आवश्यकता थी = यहाँ जैसा सच है; stackoverflow.com/questions/18962785/…
user984003

4
यह बेहतर है कि इस मामले में string.split () के बजाय shlex.split () का उपयोग करें
अलेक्सई Sviridov

4
... ( stdout=fileइस मामले में आउटपुट को फ़ाइल पर पुनर्निर्देशित करता है। यह लागू होता है > file)। ..., '>', 'file']पुनर्निर्देशन की उम्मीद करते हुए अंतिम कमांड में पास करना गलत होगा (यह शेल के बिना काम नहीं करेगा और यदि आप शेल का उपयोग करते हैं, तो आपको कमांड को एक स्ट्रिंग के रूप में पास करना चाहिए)
jfs

186

यहां पहले के उत्तरों पर कुछ विस्तार करने के लिए, कई विवरण हैं जिन्हें आमतौर पर अनदेखा किया जाता है।

  • पसंद करते हैं subprocess.run()अधिक subprocess.check_call()और दोस्तों से अधिक subprocess.call()से अधिक subprocess.Popen()से अधिक os.system()से अधिकos.popen()
  • समझें और शायद उपयोग करें text=True, उर्फ universal_newlines=True
  • के अर्थ को समझें shell=Trueया shell=Falseयह कैसे बदल जाता है और शेल की उपयुक्तता की उपलब्धता।
  • shऔर बैश के बीच अंतर को समझें
  • समझें कि एक उपप्रजाति अपने माता-पिता से कैसे अलग है, और आम तौर पर माता-पिता को नहीं बदल सकते।
  • पायथन इंटरप्रेटर को पायथन के उपप्रकार के रूप में चलाने से बचें।

इन विषयों को नीचे कुछ और विस्तार से कवर किया गया है।

पसंद करते हैं subprocess.run()याsubprocess.check_call()

subprocess.Popen()समारोह एक निम्न स्तर के workhorse है लेकिन इसे सही ढंग से उपयोग करने के लिए मुश्किल है और आप प्रतिलिपि अंत / कोड के कई पंक्तियों ... जो आसानी से पहले से ही विभिन्न प्रयोजनों के लिए कार्य आवरण उच्च स्तर का एक सेट के रूप में मानक पुस्तकालय में मौजूद चिपकाने, जो निम्नलिखित में अधिक विस्तार से प्रस्तुत किए गए हैं।

यहाँ प्रलेखन से एक पैराग्राफ है :

उपप्रोसेस को लागू करने के लिए अनुशंसित दृष्टिकोण run()सभी उपयोग के मामलों के लिए फ़ंक्शन का उपयोग करना है जो इसे संभाल सकता है। अधिक उन्नत उपयोग के मामलों के लिए, अंतर्निहित Popenइंटरफ़ेस का उपयोग सीधे किया जा सकता है।

दुर्भाग्य से, इन आवरण कार्यों की उपलब्धता पायथन संस्करणों के बीच भिन्न होती है।

  • subprocess.run()आधिकारिक तौर पर पायथन 3.5 में पेश किया गया था। यह निम्नलिखित में से सभी को बदलने के लिए है।
  • subprocess.check_output()पायथन 2.7 / 3.1 में पेश किया गया था। यह मूल रूप से इसके बराबर हैsubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
  • subprocess.check_call()पायथन 2.5 में पेश किया गया था। यह मूल रूप से इसके बराबर हैsubprocess.run(..., check=True)
  • subprocess.call()मूल subprocessमॉड्यूल ( PEP-324 ) में पायथन 2.4 में पेश किया गया था । यह मूल रूप से इसके बराबर हैsubprocess.run(...).returncode

उच्च-स्तरीय एपीआई बनाम subprocess.Popen()

रिफैक्टेड और विस्तारित subprocess.run()यह पुराने विरासत के कार्यों की तुलना में अधिक तार्किक और अधिक बहुमुखी है जो इसे बदलता है। यह एक ऐसी CompletedProcessवस्तु लौटाता है जिसमें विभिन्न विधियां होती हैं जो आपको बाहर निकलने की स्थिति, मानक आउटपुट और कुछ अन्य परिणाम और समाप्त उपप्रकार से स्थिति संकेतक प्राप्त करने की अनुमति देती हैं।

subprocess.run()अगर आपको केवल पायथन पर नियंत्रण चलाने और वापस करने के लिए एक कार्यक्रम की आवश्यकता है, तो जाने का रास्ता है अधिक सम्मिलित परिदृश्यों के लिए (पृष्ठभूमि प्रक्रियाएं, संभवतया इंटरैक्टिव I / O पायथन पैरेंट प्रोग्राम के साथ) आपको अभी भी subprocess.Popen()सभी प्लंबिंग का उपयोग करने और देखभाल करने की आवश्यकता है । इसके लिए सभी गतिशील भागों की काफी जटिल समझ की आवश्यकता होती है और इसे हल्के में नहीं लिया जाना चाहिए। सरल Popenवस्तु (संभवतः अभी भी चल रही) प्रक्रिया का प्रतिनिधित्व करती है जिसे आपके कोड से उपप्रकार के शेष जीवन के लिए प्रबंधित करने की आवश्यकता होती है।

शायद इस बात पर जोर दिया जाना चाहिए कि सिर्फ subprocess.Popen()एक प्रक्रिया है। यदि आप इसे उस पर छोड़ देते हैं, तो आपके पास पायथन के साथ समवर्ती रूप से चलने वाला एक उपप्रकार है, इसलिए एक "पृष्ठभूमि" प्रक्रिया है। यदि इसे इनपुट या आउटपुट करने या अन्यथा आपके साथ समन्वय करने की आवश्यकता नहीं है, तो यह आपके पायथन प्रोग्राम के समानांतर काम कर सकता है।

से बचें os.system()औरos.popen()

अनन्त समय के बाद से (अच्छी तरह से, अजगर 2.5 के बाद से) osमॉड्यूल प्रलेखन सिफारिश करने के लिए पसंद करते हैं निहित है subprocessसे अधिक os.system():

subprocessमॉड्यूल नई प्रक्रियाओं को उत्पन्न करने और उनके परिणामों को पुन: प्राप्त करने के लिए और अधिक शक्तिशाली सुविधाएं प्रदान करता है; इस फ़ंक्शन का उपयोग करने के लिए उस मॉड्यूल का उपयोग करना बेहतर होता है।

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

PEP-324 (जो पहले ही ऊपर उल्लेख किया गया था) में os.systemसमस्याग्रस्त क्यों है और subprocessउन मुद्दों को हल करने के प्रयासों के लिए एक अधिक विस्तृत तर्क शामिल है ।

os.popen()अधिक दृढ़ता से हतोत्साहित किया जाता है :

संस्करण 2.6 के बाद से पदावनत: यह फ़ंक्शन अप्रचलित है। subprocessमॉड्यूल का उपयोग करें ।

हालांकि, पायथन 3 में कुछ समय के बाद, इसे केवल उपयोग करने के लिए फिर से लागू किया गया है subprocess, और subprocess.Popen()विवरण के लिए प्रलेखन पर पुनर्निर्देशित किया गया है ।

समझें और आमतौर पर उपयोग करें check=True

आप यह भी देखेंगे कि subprocess.call()जैसी कई सीमाएँ हैं os.system()। नियमित उपयोग में, आपको आम तौर पर यह देखना चाहिए कि क्या प्रक्रिया सफलतापूर्वक समाप्त हो गई है, जो subprocess.check_call()और subprocess.check_output()(जहां बाद वाले भी समाप्त उपप्रकार के मानक उत्पादन को वापस लौटाते हैं)। इसी तरह, आप आमतौर पर इस्तेमाल करना चाहिए check=Trueके साथ subprocess.run()जब तक आप विशेष रूप से एक त्रुटि स्थिति लौटाने के लिए उपप्रक्रिया अनुमति देनी होगी।

अगर उपप्रोसेस नॉनजेरो एग्जिट स्टेटस देता है तो प्रैक्टिस के साथ check=Trueया subprocess.check_*, पायथन CalledProcessErrorअपवाद छोड़ देगा ।

उपप्रकार विफल होने पर डाउनस्ट्रीम कोड विफल होने पर एक सामान्य त्रुटि subprocess.run()को छोड़ना check=Trueऔर आश्चर्यचकित होना है।

दूसरी ओर, एक आम समस्या थी check_call()और यह check_output()था कि जिन उपयोगकर्ताओं ने इन कार्यों का नेत्रहीन उपयोग किया था, वे आश्चर्यचकित थे जब grepएक मैच नहीं मिलने पर अपवाद को उठाया गया था । (आपको संभवतः grepनीचे दिए गए अनुसार देशी पायथन कोड के साथ प्रतिस्थापित करना चाहिए ।)

गिनाई गई सभी चीजें, आपको यह समझने की जरूरत है कि शेल कमांड एक एग्जिट कोड कैसे लौटाते हैं, और किन शर्तों के तहत वे एक नॉन-जीरो (एरर) एग्जिट कोड लौटाएंगे, और एक सचेत फैसला करेंगे कि इसे कैसे हैंडल किया जाए।

समझें और शायद text=Trueउर्फ का उपयोग करेंuniversal_newlines=True

पायथन 3 के बाद से, पायथन से आंतरिक तार यूनिकोड के तार हैं। लेकिन इस बात की कोई गारंटी नहीं है कि एक सबप्रोसेस यूनिकोड आउटपुट या स्ट्रिंग्स को उत्पन्न करता है।

(यदि मतभेद तुरंत स्पष्ट नहीं हैं, तो नेड बैचेल्डर्स प्रैग्मेटिक यूनिकोड की सिफारिश की जाती है, यदि एकमुश्त अनिवार्य, पढ़ना नहीं है। लिंक के पीछे एक 36 मिनट की वीडियो प्रस्तुति है यदि आप चाहें, तो पेज को पढ़ने के बाद शायद खुद को कम समय लगेगा। )

नीचे गहरी, पायथन को एक bytesबफर लाना है और इसे किसी तरह व्याख्या करना है। यदि इसमें द्विआधारी डेटा का एक बूँद होता है, तो इसे यूनिकोड स्ट्रिंग में डिकोड नहीं किया जाना चाहिए , क्योंकि यह त्रुटि-प्रवण और बग-उत्प्रेरण व्यवहार है - ठीक उसी तरह का पेस्की व्यवहार जिसने कई पायथन 2 स्क्रिप्ट्स को पहेल किया, इससे पहले कि वहाँ एक तरीका था एन्कोडेड पाठ और बाइनरी डेटा के बीच ठीक से अंतर करना।

के साथ text=True, आप पायथन को बताते हैं कि आप वास्तव में, सिस्टम के डिफ़ॉल्ट एन्कोडिंग में पाठ डेटा की अपेक्षा करते हैं, और यह कि पायथन (यूनिकोड) स्ट्रिंग को पायथन की सर्वोत्तम क्षमता (आमतौर पर UTF-8) में किसी भी मामूली रूप से अपघटित किया जाना चाहिए शायद विंडोज को छोड़कर, तारीख प्रणाली?

यदि आप जो वापस मांगते हैं, वह नहीं है , तो पायथन सिर्फ आपको bytesतार stdoutऔर stderrतार देगा। कुछ पर हो सकता है कि बाद में बात आप करते हैं पता है कि वे पाठ स्ट्रिंग्स थे सब के बाद, और आप उनके एन्कोडिंग पता है। फिर, आप उन्हें डिकोड कर सकते हैं।

normal = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True,
    text=True)
print(normal.stdout)

convoluted = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))

पायथन 3.7 ने textकीवर्ड तर्क के लिए छोटे और अधिक वर्णनात्मक और समझने योग्य उपनाम पेश किया, जिसे पहले कुछ भ्रामक रूप से कहा जाता था universal_newlines

समझे shell=Trueबनामshell=False

साथ shell=Trueआप अपने खोल के लिए एक एकल स्ट्रिंग पारित, और खोल उसे वहाँ से ले जाता है।

साथ shell=Falseआप ओएस के लिए तर्कों की सूची गुजरती हैं, खोल दरकिनार।

जब आपके पास एक शेल नहीं होता है, तो आप एक प्रक्रिया को बचाते हैं और काफी हद तक छिपी हुई जटिलता से छुटकारा पा लेते हैं , जो बग्स या यहां तक ​​कि सुरक्षा समस्याओं को परेशान नहीं कर सकती है।

दूसरी ओर, जब आपके पास कोई शेल नहीं होता है, तो आपके पास पुनर्निर्देशन, वाइल्डकार्ड विस्तार, नौकरी पर नियंत्रण और बड़ी संख्या में अन्य शेल विशेषताएं नहीं होती हैं।

एक सामान्य गलती का उपयोग करना है shell=Trueऔर फिर भी पायथन को टोकन की सूची, या इसके विपरीत पास करना है। यह कुछ मामलों में काम करने के लिए होता है, लेकिन वास्तव में बीमार है और दिलचस्प तरीकों से टूट सकता है।

# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')

# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    shell=True)

# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
    shell=True)

correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    # Probably don't forget these, too
    check=True, text=True)

# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
    shell=True,
    # Probably don't forget these, too
    check=True, text=True)

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

उदाहरण का प्रतिपादन

बहुत बार, शेल की सुविधाओं को देशी पायथन कोड के साथ बदला जा सकता है। सिंपल अवाक या sedस्क्रिप्ट को शायद इसके बजाय केवल पायथन में अनुवाद करना चाहिए।

इसे आंशिक रूप से समझाने के लिए, यहाँ एक विशिष्ट लेकिन थोड़ा मूर्खतापूर्ण उदाहरण है जिसमें कई शैल विशेषताएं शामिल हैं।

cmd = '''while read -r x;
   do ping -c 3 "$x" | grep 'round-trip min/avg/max'
   done <hosts.txt'''

# Trivial but horrible
results = subprocess.run(
    cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)

# Reimplement with shell=False
with open('hosts.txt') as hosts:
    for host in hosts:
        host = host.rstrip('\n')  # drop newline
        ping = subprocess.run(
             ['ping', '-c', '3', host],
             text=True,
             stdout=subprocess.PIPE,
             check=True)
        for line in ping.stdout.split('\n'):
             if 'round-trip min/avg/max' in line:
                 print('{}: {}'.format(host, line))

यहाँ ध्यान देने योग्य कुछ बातें:

  • साथ shell=Falseआपको लगता है कि खोल तार के आसपास की आवश्यकता के हवाले जरूरत नहीं है। वैसे भी उद्धरण देना शायद एक त्रुटि है।
  • यह अक्सर एक उपप्रकार में जितना संभव हो उतना कम कोड चलाने के लिए समझ में आता है। यह आपको अपने पायथन कोड के भीतर से निष्पादन पर अधिक नियंत्रण प्रदान करता है।
  • यह कहते हुए कि, जटिल खोल पाइपलाइनें थकाऊ हैं और कभी-कभी अजगर में पुन: लागू करने के लिए चुनौतीपूर्ण हैं।

रिफलेक्टेड कोड यह भी बताता है कि शेल वास्तव में आपके लिए एक बहुत ही वाक्यविन्यास वाक्य के साथ कितना बेहतर या बदतर के लिए करता है। अजगर का कहना है स्पष्ट अंतर्निहित तुलना में बेहतर है , लेकिन अजगर कोड है बल्कि अत्यधिक शब्द और यकीनन अधिक जटिल की तुलना में यह वास्तव में है लग रहा है। दूसरी ओर, यह कई बिंदुओं की पेशकश करता है, जहां आप किसी और चीज़ के बीच में नियंत्रण को पकड़ सकते हैं, क्योंकि तुच्छता से हम बहुत आसानी से शेल कमांड आउटपुट के साथ मेजबान नाम को शामिल कर सकते हैं। (यह शेल में करने के लिए चुनौतीपूर्ण किसी भी तरह से नहीं है, या तो, लेकिन अभी तक एक और मोड़ और शायद एक और प्रक्रिया की कीमत पर।)

सामान्य शैल निर्माण

पूर्णता के लिए, यहां इन शेल विशेषताओं में से कुछ के संक्षिप्त विवरण दिए गए हैं, और कुछ नोटों पर कि वे संभवतः देशी पायथन सुविधाओं के साथ कैसे बदले जा सकते हैं।

  • ग्लोबिंग उर्फ ​​वाइल्डकार्ड विस्तार को glob.glob()साधारण पायथन स्ट्रिंग तुलनाओं के साथ या बहुत बार बदला जा सकता है for file in os.listdir('.'): if not file.endswith('.png'): continue। बश में .{png,jpg}ब्रेस विस्तार के {1..100}साथ-साथ टिल्ड विस्तार ( ~आपके घर की निर्देशिका में विस्तार, और आम तौर पर ~accountकिसी अन्य उपयोगकर्ता की होम निर्देशिका में विस्तार) जैसी कई अन्य विस्तार सुविधाएं हैं।
  • शेल वैरिएबल जैसे $SHELLया $my_exported_varकभी-कभी केवल पायथन वैरिएबल के साथ बदले जा सकते हैं। निर्यात किए गए शेल वैरिएबल उदाहरण के रूप में उपलब्ध हैं os.environ['SHELL'](इसका अर्थ exportहै उपप्रकारों को वैरिएबल उपलब्ध कराना - एक ऐसा वैरिएबल जो उपप्रोसेस को उपलब्ध नहीं है, जाहिर तौर पर शेल के सबप्रोसेस के रूप में चलने वाले पायथन के लिए उपलब्ध नहीं होगा, या इसके विपरीत। env=कीवर्ड) subprocessविधियों का तर्क आपको उपप्रकार के वातावरण को एक शब्दकोश के रूप में परिभाषित करने की अनुमति देता है, इसलिए यह एक तरीका है कि पायथन चर को उपप्रोसेस के लिए दृश्यमान बनाया जाए)। साथ shell=Falseआप किसी भी उद्धरण निकालने का तरीका समझने की जरूरत होगी; उदाहरण के लिए, निर्देशिका नाम के आसपास के उद्धरणों के बिना cd "$HOME"समान है os.chdir(os.environ['HOME'])। (अक्सरcdवैसे भी उपयोगी या आवश्यक नहीं है, और कई शुरुआती लोग चर के आसपास दोहरे उद्धरण चिह्नों को छोड़ देते हैं और एक दिन तक इसके साथ दूर हो जाते हैं ... )
  • पुनर्निर्देशन आपको अपने मानक इनपुट के रूप में एक फ़ाइल से पढ़ने की अनुमति देता है, और एक फ़ाइल के लिए अपना मानक आउटपुट लिखता है। लिखने के लिए और पढ़ने के लिए grep 'foo' <inputfile >outputfileखुलता है , और इसकी सामग्री को मानक इनपुट के रूप में पास करता है , जिसका मानक आउटपुट तब भूमि में होता है । यह आमतौर पर मूल पायथन कोड के साथ बदलने के लिए मुश्किल नहीं है।outputfileinputfilegrepoutputfile
  • पाइपलाइन पुनर्निर्देशन का एक रूप हैं। echo foo | nlदो उपप्रकार चलाता है, जहां का मानक आउटपुट (ओएस स्तर पर, यूनिक्स की तरह सिस्टम echoमें मानक इनपुट है nl, यह एक सिंगल फाइल हैंडल है)। यदि आप पाइप लाइन के एक या दोनों छोरों को देशी पायथन कोड से नहीं बदल सकते हैं, तो शायद सभी के बाद एक शेल का उपयोग करने के बारे में सोचें, खासकर अगर पाइपलाइन की दो या तीन से अधिक प्रक्रियाएं हैं (हालांकि pipesपायथन मानक पुस्तकालय या एक नंबर में मॉड्यूल को देखें) अधिक आधुनिक और बहुमुखी तीसरे पक्ष के प्रतियोगियों)।
  • नौकरी नियंत्रण आपको नौकरियों को बाधित करने, उन्हें पृष्ठभूमि में चलाने, उन्हें अग्रभूमि में वापस करने आदि की अनुमति देता है। एक प्रक्रिया को रोकने और जारी रखने के लिए मूल यूनिक्स संकेत निश्चित रूप से पायथन से उपलब्ध हैं। लेकिन नौकरी शेल में एक उच्च-स्तरीय अमूर्तता है जिसमें प्रक्रिया समूह आदि शामिल हैं जिन्हें आपको समझना होगा कि क्या आप पायथन से ऐसा कुछ करना चाहते हैं।
  • जब तक आप समझते हैं कि शेल मूल रूप से एक स्ट्रिंग है, शेल में उद्धृत करना संभावित रूप से भ्रमित करना है। तो ls -l /बराबर है, 'ls' '-l' '/'लेकिन शाब्दिक के आसपास उद्धृत पूरी तरह से वैकल्पिक है। अछूता तार जिसमें शेल मेटाचैकर होते हैं जो पैरामीटर विस्तार, व्हाट्सएप टोकन और वाइल्डकार्ड विस्तार से गुजरते हैं; डबल कोट्स व्हॉट्सएप टोकेनाइजेशन और वाइल्डकार्ड विस्तार को रोकते हैं लेकिन पैरामीटर विस्तार (चर प्रतिस्थापन, कमांड प्रतिस्थापन और बैकस्लैश प्रसंस्करण) की अनुमति देते हैं। यह सिद्धांत में सरल है, लेकिन भयावह हो सकता है, खासकर जब व्याख्या की कई परतें होती हैं (उदाहरण के लिए एक दूरस्थ शेल कमांड)।

shऔर बैश के बीच अंतर को समझें

subprocess/bin/shजब तक आप विशेष रूप से अनुरोध नहीं करते हैं तब तक अपना शेल कमांड चलाता है (विंडोज पर पाठ्यक्रम को छोड़कर, जहां यह COMSPECचर के मूल्य का उपयोग करता है )। इसका अर्थ है कि विभिन्न बैश-केवल सुविधाएँ जैसे सरणियाँ, [[आदि उपलब्ध नहीं हैं।

यदि आपको बैश-केवल सिंटैक्स का उपयोग करने की आवश्यकता है, तो आप शेल में पथ को पास कर सकते हैं executable='/bin/bash'( जैसे कि यदि आपका बैश कहीं और स्थापित है, तो आपको पथ को समायोजित करने की आवश्यकता है)।

subprocess.run('''
    # This for loop syntax is Bash only
    for((i=1;i<=$#;i++)); do
        # Arrays are Bash-only
        array[i]+=123
    done''',
    shell=True, check=True,
    executable='/bin/bash')

A subprocessअपने माता-पिता से अलग है, और इसे बदल नहीं सकता है

कुछ सामान्य गलती कुछ ऐसा कर रही है

subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True)  # Doesn't work

लालित्य की कमी से हटकर "सबप्रोसेस" नाम के "उप" भाग को समझने की एक मूलभूत कमी भी है।

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

इस विशेष मामले में तत्काल सुधार एक ही उपप्रकार में दोनों कमांड चलाने के लिए है;

subprocess.run('foo=bar; echo "$foo"', shell=True)

हालांकि स्पष्ट रूप से इस विशेष उपयोग के मामले में शेल की आवश्यकता नहीं है। याद रखें, आप वर्तमान प्रक्रिया (और इस प्रकार इसके बच्चों) के वातावरण में फेरबदल कर सकते हैं

os.environ['foo'] = 'bar'

या के साथ एक बच्चे की प्रक्रिया के लिए एक वातावरण सेटिंग पास

subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})

(स्पष्ट रीफैक्टरिंग का उल्लेख नहीं करना subprocess.run(['echo', 'bar']); लेकिन echoपहली जगह में एक उपप्रकार में चलाने के लिए कुछ का एक खराब उदाहरण है, निश्चित रूप से)।

पायथन से पायथन भागो मत

यह थोड़ा संदिग्ध सलाह है; निश्चित रूप से ऐसी स्थितियाँ हैं जहाँ यह समझ में आता है या पायथन इंटरप्रेटर को पायथन स्क्रिप्ट से उपप्रकार के रूप में चलाने के लिए एक परम आवश्यकता है। लेकिन बहुत बार, सही दृष्टिकोण केवल importआपके कॉलिंग स्क्रिप्ट में अन्य पायथन मॉड्यूल के लिए होता है और सीधे इसके कार्यों को कॉल करता है।

यदि अन्य पायथन स्क्रिप्ट आपके नियंत्रण में है, और यह एक मॉड्यूल नहीं है, तो इसे एक में बदलने पर विचार करें । (यह उत्तर बहुत लंबा है, इसलिए मैं यहाँ विवरण में नहीं दूंगा।)

यदि आपको समानता की आवश्यकता है, तो आप multiprocessingमॉड्यूल के साथ सबप्रोसेस में पायथन फ़ंक्शन चला सकते हैं ऐसा भी है threadingजो एक प्रक्रिया में कई कार्य करता है (जो अधिक हल्का है और आपको अधिक नियंत्रण प्रदान करता है, लेकिन एक प्रक्रिया के भीतर उस थ्रेड में अधिक विवश भी कसकर युग्मित होते हैं, और एकल GIL से बाध्य होते हैं ।)


2
एक विस्तृत विवरण के रूप में आप पायथन को उपप्रकार के रूप में कॉल करने से कैसे बच सकते हैं, इस उत्तर को मूर्त रूप से समान प्रश्न पर देखें।
ट्रिपलए

4
यह मेरे दिमाग को चकरा देता है कि मुझे इस तरह के मूल प्रश्न का एक नया उत्तर पोस्ट करना था ताकि यह दिखाया जा सके कि प्रश्न को मुहावरे से कमांड कैसे चलाया जाए। आपका उत्तर लंबा है, लेकिन मुझे ऐसा कोई उदाहरण दिखाई नहीं दे रहा है। असंबंधित: कार्गो-खेती से बचें। यदि आपके मामले में check_call () काम करता है, तो इसका उपयोग करें। मुझे एक कोड को ठीक करना था जो run()नेत्रहीन रूप से उपयोग करता था । गुम check=Trueहोने से एक बग पैदा हो जाता है, जिसे अगर check_call का उपयोग किया जाता है तो इससे बचा जा सकता है - "check" नाम में है, आप इसे नहीं खो सकते हैं - यह सही डिफ़ॉल्ट है: चुपचाप त्रुटियों को अनदेखा न करें। मैं आगे नहीं पढ़ा।
jfs

1
@jfs प्रतिक्रिया के लिए धन्यवाद, मैं वास्तव में बैश बनाम के बारे में एक खंड जोड़ने की योजना बना रहा था shलेकिन आपने मुझे इसके लिए हरा दिया। मैं उन शुरुआती लोगों की मदद करने के लिए बारीकियों को विस्तार से बताने की कोशिश कर रहा हूं जिनके लिए ये नुकसान स्पष्ट नहीं हैं, इसलिए थोड़ा लंबा हो जाता है। तुम्हारा होना काफी पर्याप्त होना चाहिए अन्यथा; +1
ट्रिपल नाऊ

क्या stderr/stdout = subprocess.PIPEडिफ़ॉल्ट सेटिंग्स की तुलना में उच्च प्रदर्शन ओवरहेड है?
स्ट्रिंगर

1
@ स्ट्रिंगर्स का मैंने परीक्षण नहीं किया है, लेकिन मैं यह नहीं देखता कि ऐसा क्यों होना चाहिए। यदि आप उन पाइपों को किसी ऐसी चीज से जोड़ते हैं जो कुछ प्रसंस्करण करती है, तो निश्चित रूप से उस प्रसंस्करण के लिए ओई की आवश्यकता होती है; लेकिन यह पाइप में ही नहीं होता है। डिफ़ॉल्ट स्टैडआउट या स्टेडर को बिल्कुल भी कैप्चर नहीं करना है, अर्थात जो कुछ भी प्रिंट होता है वह पाइथन की दृश्यता और नियंत्रण से बाहर है, जैसे कि os.system()
3

41

इसे सबप्रोसेस के साथ कहें

import subprocess
subprocess.Popen("cwm --rdf test.rdf --ntriples > test.nt")

आपको जो त्रुटि हो रही है, ऐसा लगता है क्योंकि सर्वर पर कोई स्वैप मॉड्यूल नहीं है, आपको सर्वर पर स्वैप स्थापित करना चाहिए फिर स्क्रिप्ट को फिर से चलाना चाहिए


3
swapमॉड्यूल, जाहिर है वहाँ कारण है कि शेल कार्यों से आदेश चलाकर।
स्वेन मार्नाच

2
सर्वर पर नहीं, जब वह सर्वर पर चलाता है तो आयात त्रुटि होती है।
जैकब बोएयर

@ mkn: "तब मैंने बस उस आउटपुट को कॉपी किया और एक कॉपी पेस्ट टर्मिनल में किया और एंटर मारा और यह काम किया ..." - क्या आपने सर्वर पर या अपने मशीन पर यह कोशिश की?
स्वेन मार्नाच

क्या आप इसे अकेले कंप्यूटर स्टैंड पर चला रहे हैं लेकिन जब आप इसे अपने सर्वर पर चलाते हैं तो यह काम नहीं कर रहा है? या क्या आप इसे सर्वर टर्मिनल पर नहीं बल्कि स्वयं सर्वर पर चलाने में सक्षम हैं
Jakob Bowyer

1
यह गलत है यदि आप उपयोग नहीं करते हैं, shell=Trueतो आपको एक सूची का उपयोग करने के लिए एक से अधिक तर्क पास करना चाहिए, ['a', 'b', 'c']इसके बजाय उपयोग करना चाहिए 'a b c'। हालाँकि > file, कमांड में (शेल पुनर्निर्देशन) के कारण एक भोली विभाजन काम नहीं करेगा । अधिक जानकारी
jfs

18

यह संभव है कि आप कमांड को निष्पादित करने के लिए पैरामीटर -c के साथ बैश प्रोग्राम का उपयोग करें:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
output = subprocess.check_output(['bash','-c', bashCommand])

2
subprocess.check_output(bashCommand, shell=True)एक ही काम करता है। यदि आपकी कमांड एक स्थिर स्ट्रिंग है, तो इसे स्वयं सूची में पार्स करने का प्रयास करें और इससे बचें shell=True; हालाँकि इस मामले में आपको वैसे भी पुनर्निर्देशन के लिए शेल की आवश्यकता है, या फिर आपको इसे शुद्ध पायथन में with open('test.nt', 'w') as dest: output = subprocess.check_output(['cwm' ,'--rdf', 'test.rdf', '--ntriples'], stdout=dest, shell=False)
रिफलेक्टर

@tripleee नोट: /bin/sh( उपप्रकार द्वारा प्रयुक्त) जरूरी नहीं है bash(आप बशीज़ का उपयोग नहीं कर सकते हैं)। हालांकि executable='/bin/bashअगर एक का उपयोग कर सकते हैं । यहाँ एक कोड उदाहरण है
jfs

2
यह पहला उत्तर है जहां कमांड को सफलतापूर्वक शुरू होना चाहिए (स्वीकृत और दूसरा लोकप्रिय उत्तर केवल गलत हैं। एक छोटी सी बात है: check_output()यहां बेकार है ( > fileरीडायरेक्शन के कारण आउटपुट हमेशा खाली रहता है । इसके बजाय उपयोग करें check_call()
jfs

16

आप उपयोग कर सकते हैं subprocess, लेकिन मैंने हमेशा महसूस किया कि यह ऐसा करने का 'पाइथोनिक' तरीका नहीं था। इसलिए मैंने सुल्तान (बेशर्म प्लग) बनाया जो कमांड लाइन फ़ंक्शन को चलाना आसान बनाता है।

https://github.com/aeroxis/sultan


3
बहुत बढ़िया! बहुत अधिक क्लीनर और उपप्रकार से अधिक सहज।
mjd2

आपको बहुत - बहुत धन्यवाद! मैं यह सुनकर प्रसन्न हूँ कि!
डेविड डैनियल

2
इसे ईमानदारी से मानक पुस्तकालय में अपनाया जाना चाहिए।
जोशुआ डेटेलर

1
क्या सुल्तान के उपयोग से टर्मिनल से आउटपुट कैप्चर करने का कोई तरीका है?
अल्व्स

हाँ आप @alvas कर सकते हैं ... यहाँ डॉक्स कैसे करना है: sultan.readthedocs.io/en/latest/…
डेविड डैनियल

7

त्रुटि के अनुसार आपको सर्वर पर स्वैप नाम का एक पैकेज याद आ रहा है । इसके /usr/bin/cwmलिए इसकी आवश्यकता है। यदि आप उबंटू / डेबियन पर हैं, तो python-swapएप्टीट्यूड का उपयोग करके इंस्टॉल करें।


लेकिन यह काम करता है जब मैं इसे सीधे टर्मिनल में चलाता हूं ... तो स्वैप वहां होना चाहिए, नहीं?
mkn

दो विकल्प हैं। या तो यह नहीं मिल सकता है swapया इसे पहली जगह में आयात नहीं करना चाहिए था। आप import swapमैन्युअल रूप से कर सकते हैं ? क्या यह काम करता है?
किचन

हम्म मैं नहीं कर सकता। अगर मैं टर्मिनल में अजगर टाइप करने के साथ अजगर शुरू करता हूं और फिर मैं आयात स्वैप टाइप करता हूं, तो मुझे त्रुटि मिली "ImportError: कोई मॉड्यूल जिसका नाम स्वैप नहीं है"। अजीब बात यह है कि यह तब भी काम करता है जब मैं cwm कमांड को सीधे सर्वर के टर्मिनल में
चलाता हूं

मुद्रण की कोशिश करें sys.pathकि यह कहाँ काम कर रहा है और कहाँ नहीं है। फिर प्रिंट किए गए फ़ोल्डरों में स्वैप फ़ोल्डर या swap.py की तलाश करें। जैसा कि स्वेन ने कहा, उन रास्तों के साथ कोई समस्या हो सकती है, और इससे आपको यह पता लगाने में मदद मिलेगी।
किचन

4

इसके अलावा आप 'os.popen' का उपयोग कर सकते हैं। उदाहरण:

import os

command = os.popen('ls -al')
print(command.read())
print(command.close())

आउटपुट:

total 16
drwxr-xr-x 2 root root 4096 ago 13 21:53 .
drwxr-xr-x 4 root root 4096 ago 13 01:50 ..
-rw-r--r-- 1 root root 1278 ago 13 21:12 bot.py
-rw-r--r-- 1 root root   77 ago 13 21:53 test.py

None

1
दस्तावेज़ में एक बड़ा लाल बॉक्स है: " संस्करण 2.6 सेsubprocess
ट्रिपल

1
निष्पक्षता में, os.popenअब यह चेतावनी नहीं है, और subprocess.Popen()अब चारों ओर एक पतली आवरण है ।
ट्रिपलआई

4

शेल के बिना कमांड को चलाने के लिए, कमांड को एक सूची के रूप में पास करें और पायथन में पुनर्निर्देशन को लागू करें [subprocess]:

#!/usr/bin/env python
import subprocess

with open('test.nt', 'wb', 0) as file:
    subprocess.check_call("cwm --rdf test.rdf --ntriples".split(),
                          stdout=file)

नोट: > test.ntअंत में नहीं। stdout=fileपुनर्निर्देशन को लागू करता है।


पायथन में शेल का उपयोग करके कमांड चलाने के लिए, कमांड को एक स्ट्रिंग के रूप में पास करें और सक्षम करें shell=True:

#!/usr/bin/env python
import subprocess

subprocess.check_call("cwm --rdf test.rdf --ntriples > test.nt",
                      shell=True)

यहां शेल आउटपुट रिडायरेक्शन ( > test.ntकमांड में है) के लिए जिम्मेदार है।


एक bash कमांड को चलाने के लिए है कि का उपयोग करता है bashisms, बैश निष्पादन स्पष्ट रूप से जैसे, करने के लिए निर्दिष्ट अनुकरण बैश प्रक्रिया प्रतिस्थापन :

#!/usr/bin/env python
import subprocess

subprocess.check_call('program <(command) <(another-command)',
                      shell=True, executable='/bin/bash')

शायद उल्लेख है कि .split()पर्याप्त नहीं हैं जब उद्धृत तार आदि होते हैं। एक अलग दिनचर्या होती है shlex.split()जो मनमाने ढंग से जटिल खोल सिंटैक्स के साथ होती है।
ट्रिपलए

@tripleee .split()इस मामले में काम करता है। shlex.split()कभी-कभी उपयोगी हो सकता है लेकिन यह कुछ मामलों में भी विफल हो सकता है। बहुत सी बातें हैं जिनका उल्लेख किया जा सकता है। आप ऊपर दिए गए सबप्रोसेस टैग विवरण के लिंक से शुरू कर सकते हैं।
jfs

0

ऐसा करने का पायथोनिक तरीका उपयोग कर रहा है subprocess.Popen

subprocess.Popen एक सूची लेता है जहां पहला तत्व कमांड लाइन तर्क के बाद चलने वाला कमांड है।

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

import subprocess

args = ['echo', 'Hello!']
subprocess.Popen(args) // same as running `echo Hello!` on cmd line

args2 = ['echo', '-v', '"Hello Again"']
subprocess.Popen(args2) // same as running 'echo -v "Hello Again!"` on cmd line

नहीं, अंतिम उदाहरण echo -v '"Hello Again!"'दोहरे उद्धरण चिह्नों के आसपास एकल उद्धरण के साथ चलने के समान है ।
ट्रिपल

इसके अलावा, सही ढंग से उपयोग करने के लिए subprocesss.Popen, आपको परिणामी प्रक्रिया ऑब्जेक्ट का प्रबंधन करना होगा (कम से कम, wait()इसे ज़ोंबी प्रक्रिया में बदलने से रोकने के लिए प्रदर्शन करें )।
ट्रिपलआई
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.