मैं पायथन से अतुल्यकालिक रूप से एक बाहरी कमांड कैसे चला सकता हूं?


120

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

मैंने इस पोस्ट को पढ़ा:

पायथन में एक बाहरी कमांड को कॉल करना

मैं फिर से चला गया और कुछ परीक्षण किया, और ऐसा लगता os.system()है कि काम करेगा बशर्ते कि मैं &कमांड के अंत में उपयोग करता हूं ताकि मुझे इसे वापस आने के लिए इंतजार न करना पड़े। क्या मैं सोच रहा हूँ कि क्या यह इस तरह के कार्य को पूरा करने का उचित तरीका है? मैंने कोशिश की commands.call()लेकिन यह मेरे लिए काम नहीं करेगा क्योंकि यह बाहरी कमांड पर ब्लॉक होता है।

कृपया मुझे बताएं कि क्या इसके लिए उपयोग os.system()करना उचित है या यदि मुझे कोई अन्य मार्ग आज़माना चाहिए।

जवाबों:


135

subprocess.Popen ठीक वही करता है जो आप चाहते हैं।

from subprocess import Popen
p = Popen(['watch', 'ls']) # something long running
# ... do other stuff while subprocess is running
p.terminate()

(टिप्पणियों से जवाब पूरा करने के लिए संपादित करें)

पोपेन उदाहरण आप की तरह कई अन्य चीजें भी कर सकते हैं, poll()यह देखने के लिए कि क्या यह अभी भी चल रहा है, और आप communicate()इसे स्टड पर डेटा भेजने के लिए कर सकते हैं , और इसके समाप्त होने की प्रतीक्षा कर सकते हैं।


4
आप यह भी जांच कर सकते हैं कि बच्चे की प्रक्रिया समाप्त हो गई है या नहीं, या नहीं, यह जांचने के लिए प्रतीक्षा () का उपयोग करें।
एडम रोसेनफील्ड

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

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

14
संवाद () और प्रतीक्षा () संचालन अवरुद्ध कर रहे हैं, यद्यपि। आप ओपी की तरह आदेशों को समानांतर नहीं कर पाएंगे, यदि आप उनका उपयोग करते हैं तो पूछ सकते हैं।
cdleary

1
Cdleary पूरी तरह से सही है, यह उल्लेख किया जाना चाहिए कि संवाद करें और प्रतीक्षा करें ब्लॉक करें, इसलिए इसे केवल तब करें जब आप चीजों को बंद करने की प्रतीक्षा कर रहे हों। (जो आपको वास्तव में अच्छी तरह से व्यवहार करने के लिए करना चाहिए)
अली अफशर

48

यदि आप समानांतर में कई प्रक्रियाएँ चलाना चाहते हैं और फिर परिणाम आने पर उन्हें संभालते हैं, तो आप निम्नलिखित की तरह मतदान का उपयोग कर सकते हैं:

from subprocess import Popen, PIPE
import time

running_procs = [
    Popen(['/usr/bin/my_cmd', '-i %s' % path], stdout=PIPE, stderr=PIPE)
    for path in '/tmp/file0 /tmp/file1 /tmp/file2'.split()]

while running_procs:
    for proc in running_procs:
        retcode = proc.poll()
        if retcode is not None: # Process finished.
            running_procs.remove(proc)
            break
        else: # No process is done, wait a bit and check again.
            time.sleep(.1)
            continue

    # Here, `proc` has finished with return code `retcode`
    if retcode != 0:
        """Error handling."""
    handle_results(proc.stdout)

नियंत्रण प्रवाह थोड़ा जटिल है क्योंकि मैं इसे छोटा करने की कोशिश कर रहा हूं - आप अपने स्वाद के लिए रिफ्लेक्टर कर सकते हैं। :-)

इससे शुरुआती-परिष्करण अनुरोधों को सर्व करने का लाभ मिलता है। यदि आप communicateपहले चलने की प्रक्रिया पर कॉल करते हैं और जो सबसे लंबे समय तक चलने के लिए निकलता है, तो अन्य चल रही प्रक्रियाएँ बेकार बैठी होंगी जब आप उनके परिणामों को संभाल रहे होंगे।


3
@ यह निर्भर करता है कि आप व्यस्त-प्रतीक्षा को कैसे परिभाषित करते हैं। देखें कि व्यस्त-प्रतीक्षा और मतदान में क्या अंतर है?
पियोत्र डोब्रोगोस्ट

1
क्या केवल एक ही नहीं प्रक्रियाओं के एक सेट को प्रदूषित करने का कोई तरीका है?
पियोत्र डोब्रोगोस्ट

1
ध्यान दें: यह लटका हो सकता है अगर कोई प्रक्रिया पर्याप्त आउटपुट उत्पन्न करती है। यदि आप PIPE का उपयोग करते हैं, तो आपको समवर्ती रूप से उपभोग करना चाहिए (इसके बारे में सबप्रोसेस डॉक्स में चेतावनियाँ हैं (बहुत अधिक लेकिन पर्याप्त नहीं)।
JFS

@PiotrDobrogost: आप os.waitpidसीधे उपयोग कर सकते हैं जो यह जांचने की अनुमति देता है कि क्या किसी बच्चे की प्रक्रिया ने इसकी स्थिति बदल दी है।
jfs

5
के ['/usr/bin/my_cmd', '-i', path]बजाय का उपयोग करें['/usr/bin/my_cmd', '-i %s' % path]
jfs

11

अगर मैं सोच रहा हूँ कि क्या यह [os.system ()] इस तरह की चीज़ को पूरा करने का उचित तरीका है?

सं os.system()उचित तरीका नहीं है। इसलिए हर कोई उपयोग करने के लिए कहता हैsubprocess

अधिक जानकारी के लिए, http://docs.python.org/library/os.html#os.system पढ़ें

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


8

मुझे asyncproc मॉड्यूल के साथ अच्छी सफलता मिली है , जो प्रक्रियाओं से आउटपुट के साथ अच्छी तरह से संबंधित है। उदाहरण के लिए:

import os
from asynproc import Process
myProc = Process("myprogram.app")

while True:
    # check to see if process has ended
    poll = myProc.wait(os.WNOHANG)
    if poll is not None:
        break
    # print any new output
    out = myProc.read()
    if out != "":
        print out

यह गितुब कहीं भी है?
निक

यह gpl लाइसेंस है, इसलिए मुझे यकीन है कि यह कई बार वहां पर होगा। यहाँ एक है: github.com/albertz/helpers/blob/master/asyncproc.py
Noah

मैंने python3 के साथ काम करने के लिए कुछ संशोधनों के साथ एक जिस्ट जोड़ा। (ज्यादातर स्ट्रट्स को बाइट्स से बदल देता है)। देखें gist.github.com/grandemk/cbc528719e46b5a0ffbd07e3054aab83
टिक

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

7

गैर-अवरुद्ध रीडलाइन के साथ pexpect का उपयोग करना ऐसा करने का एक और तरीका है। Pexpect गतिरोध समस्याओं को हल करता है, आपको पृष्ठभूमि में प्रक्रियाओं को आसानी से चलाने की अनुमति देता है, और कॉलबैक करने के लिए आसान तरीके देता है जब आपकी प्रक्रिया पूर्वनिर्धारित स्ट्रिंग्स को बाहर निकालती है, और आमतौर पर प्रक्रिया को बहुत आसान बनाती है।


4

"मुझे इसे वापस करने के लिए इंतजार करने की ज़रूरत नहीं है" यह देखते हुए, सबसे आसान समाधानों में से एक यह होगा:

subprocess.Popen( \
    [path_to_executable, arg1, arg2, ... argN],
    creationflags = subprocess.CREATE_NEW_CONSOLE,
).pid

लेकिन ... इससे जो मैंने पढ़ा है, वह "इस तरह के काम को पूरा करने का उचित तरीका नहीं है" क्योंकि सुरक्षा जोखिमों से पैदा होता है subprocess.CREATE_NEW_CONSOLE झंडा ।

यहां होने वाली प्रमुख चीजें subprocess.CREATE_NEW_CONSOLEनए कंसोल और .pid(रिटर्न प्रक्रिया आईडी बनाने के लिए उपयोग की जाती हैं ताकि आप बाद में कार्यक्रम की जांच कर सकें यदि आप चाहते हैं) तो यह करें कि अपना काम खत्म करने के लिए कार्यक्रम की प्रतीक्षा न करें।


3

मुझे वही समस्या है जो पायथन में s3270 स्क्रिप्टिंग सॉफ्टवेयर का उपयोग करके 3270 टर्मिनल से जुड़ने की कोशिश कर रहा है। अब मैं इस प्रक्रिया के एक उपवर्ग के साथ समस्या को हल कर रहा हूं जो मुझे यहां मिली:

http://code.activestate.com/recipes/440554/

और यहाँ फ़ाइल से लिया गया नमूना है:

def recv_some(p, t=.1, e=1, tr=5, stderr=0):
    if tr < 1:
        tr = 1
    x = time.time()+t
    y = []
    r = ''
    pr = p.recv
    if stderr:
        pr = p.recv_err
    while time.time() < x or r:
        r = pr()
        if r is None:
            if e:
                raise Exception(message)
            else:
                break
        elif r:
            y.append(r)
        else:
            time.sleep(max((x-time.time())/tr, 0))
    return ''.join(y)

def send_all(p, data):
    while len(data):
        sent = p.send(data)
        if sent is None:
            raise Exception(message)
        data = buffer(data, sent)

if __name__ == '__main__':
    if sys.platform == 'win32':
        shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n')
    else:
        shell, commands, tail = ('sh', ('ls', 'echo HELLO WORLD'), '\n')

    a = Popen(shell, stdin=PIPE, stdout=PIPE)
    print recv_some(a),
    for cmd in commands:
        send_all(a, cmd + tail)
        print recv_some(a),
    send_all(a, 'exit' + tail)
    print recv_some(a, e=0)
    a.wait()

3

स्वीकृत उत्तर बहुत पुराना है।

मुझे यहाँ एक बेहतर आधुनिक उत्तर मिला:

https://kevinmccarthy.org/2016/07/25/streaming-subprocess-stdin-and-stdout-with-asyncio-in-python/

और कुछ बदलाव किए:

  1. यह खिड़कियों पर काम करते हैं
  2. इसे कई कमांड के साथ काम करें
import sys
import asyncio

if sys.platform == "win32":
    asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())


async def _read_stream(stream, cb):
    while True:
        line = await stream.readline()
        if line:
            cb(line)
        else:
            break


async def _stream_subprocess(cmd, stdout_cb, stderr_cb):
    try:
        process = await asyncio.create_subprocess_exec(
            *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
        )

        await asyncio.wait(
            [
                _read_stream(process.stdout, stdout_cb),
                _read_stream(process.stderr, stderr_cb),
            ]
        )
        rc = await process.wait()
        return process.pid, rc
    except OSError as e:
        # the program will hang if we let any exception propagate
        return e


def execute(*aws):
    """ run the given coroutines in an asyncio loop
    returns a list containing the values returned from each coroutine.
    """
    loop = asyncio.get_event_loop()
    rc = loop.run_until_complete(asyncio.gather(*aws))
    loop.close()
    return rc


def printer(label):
    def pr(*args, **kw):
        print(label, *args, **kw)

    return pr


def name_it(start=0, template="s{}"):
    """a simple generator for task names
    """
    while True:
        yield template.format(start)
        start += 1


def runners(cmds):
    """
    cmds is a list of commands to excecute as subprocesses
    each item is a list appropriate for use by subprocess.call
    """
    next_name = name_it().__next__
    for cmd in cmds:
        name = next_name()
        out = printer(f"{name}.stdout")
        err = printer(f"{name}.stderr")
        yield _stream_subprocess(cmd, out, err)


if __name__ == "__main__":
    cmds = (
        [
            "sh",
            "-c",
            """echo "$SHELL"-stdout && sleep 1 && echo stderr 1>&2 && sleep 1 && echo done""",
        ],
        [
            "bash",
            "-c",
            "echo 'hello, Dave.' && sleep 1 && echo dave_err 1>&2 && sleep 1 && echo done",
        ],
        [sys.executable, "-c", 'print("hello from python");import sys;sys.exit(2)'],
    )

    print(execute(*runners(cmds)))

यह संभावना नहीं है कि उदाहरण कमांड आपके सिस्टम पर पूरी तरह से काम करेगा, और यह अजीब त्रुटियों को संभालता नहीं है, लेकिन यह कोड एसिंको का उपयोग करके कई सबप्रोसेस को चलाने और आउटपुट को स्ट्रीम करने का एक तरीका प्रदर्शित करता है।


मैंने इसे विंडोज़ पर चल रहे cpython 3.7.4 और उबंटू WSL और देशी अल्पाइन लिनक्स पर चलने वाले 3.7pyon पर परीक्षण किया है
Terrel Shumway


1

यहाँ कई उत्तर हैं लेकिन उनमें से किसी ने भी मेरी आवश्यकताओं को पूरा नहीं किया है:

  1. मैं अपने टर्मिनल को उपप्रोसेस आउटपुट के साथ समाप्त या प्रदूषित करने के लिए कमांड का इंतजार नहीं करना चाहता।

  2. मैं बैश स्क्रिप्ट को रीडायरेक्ट के साथ चलाना चाहता हूं।

  3. मैं अपनी बैश स्क्रिप्ट (उदाहरण के लिए find ... | tar ...) के भीतर पाइपिंग का समर्थन करना चाहता हूं ।

आवश्यकताओं से ऊपर केवल एकमात्र संयोजन है:

subprocess.Popen(['./my_script.sh "arg1" > "redirect/path/to"'],
                 stdout=subprocess.PIPE, 
                 stderr=subprocess.PIPE,
                 shell=True)

0

यह "अतुल्यकालिक रूप से समाप्त करने के लिए आदेश की प्रतीक्षा करें" के तहत पायथन 3 सबप्रोसेस उदाहरणों द्वारा कवर किया गया है।

import asyncio

proc = await asyncio.create_subprocess_exec(
    'ls','-lha',
    stdout=asyncio.subprocess.PIPE,
    stderr=asyncio.subprocess.PIPE)

# do something else while ls is working

# if proc takes very long to complete, the CPUs are free to use cycles for 
# other processes
stdout, stderr = await proc.communicate()

प्रक्रिया await asyncio.create_subprocess_exec(...)पूरी होते ही चालू हो जाएगी । यदि यह आपके द्वारा कॉल किए गए समय से समाप्त नहीं हुआ है await proc.communicate(), तो यह आपको अपनी आउटपुट स्थिति देने के लिए इंतजार करेगा। यदि यह समाप्त हो गया है, proc.communicate()तो तुरंत वापस आ जाएगा।

यहाँ का सार टेरेल्स उत्तर के समान है लेकिन मुझे लगता है कि टेरेल्स उत्तर चीजों को ओवरकम्प्लीट करने के लिए प्रकट होता है।

asyncio.create_subprocess_execअधिक जानकारी के लिए देखें ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.