كيف يمكنني تشغيل أمر خارجي بشكل غير متزامن من بايثون؟

StackOverflow https://stackoverflow.com/questions/636561

سؤال

أحتاج إلى تشغيل أمر Shell بشكل غير متزامن من برنامج Python النصي.أعني بهذا أنني أريد أن يستمر تشغيل برنامج Python النصي أثناء تشغيل الأمر الخارجي والقيام بكل ما يلزم القيام به.

قرأت هذا المنشور :

استدعاء أمر خارجي في بايثون

ثم ذهبت وأجريت بعض الاختبارات، ويبدو الأمر كذلك os.system() سوف أقوم بهذه المهمة بشرط أن أستخدمها & في نهاية الأمر حتى لا أضطر إلى انتظار عودته.ما أتساءل عنه هو ما إذا كانت هذه هي الطريقة الصحيحة لإنجاز مثل هذا الشيء؟حاولت commands.call() لكنه لن يعمل معي لأنه يمنع الأمر الخارجي.

واسمحوا لي أن أعرف إذا كنت تستخدم os.system() فهذا أمر مستحسن أو إذا كان علي تجربة طريق آخر.

هل كانت مفيدة؟

المحلول

عملية فرعية.فتح يفعل بالضبط ما تريد.

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

(تحرير لإكمال الإجابة من التعليقات)

يمكن لمثيل Popen القيام بأشياء أخرى مختلفة مثلك poll() لمعرفة ما إذا كان لا يزال قيد التشغيل، ويمكنك communicate() معه لإرسال البيانات على stdin، وانتظر حتى ينتهي.

نصائح أخرى

إذا كنت ترغب في تشغيل العديد من العمليات بالتوازي ومن ثم التعامل معها عندما تسفر عن نتائج، يمكنك استخدام الاقتراع كما هو الحال في ما يلي:

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 على عملية التشغيل الأولى والتي تبين لتشغيل أطول، وعمليات أخرى قيد التشغيل سيكون قد تم يجلس هناك الخمول عندما كنت قد تم التعامل مع نتائجها.

ما أتساءل عنه هو ما إذا كانت هذه [os.system()] هي الطريقة الصحيحة لإنجاز مثل هذا الشيء؟

لا. os.system() ليست الطريقة الصحيحة.لهذا السبب يقول الجميع للاستخدام subprocess.

لمزيد من المعلومات، اقرأ http://docs.python.org/library/os.html#os.system

توفر وحدة المعالجة الفرعية مرافق أكثر قوة لتفريغ العمليات الجديدة واسترداد نتائجها ؛استخدام هذه الوحدة هو الأفضل لاستخدام هذه الوظيفة.استخدم وحدة المعالجة الفرعية.تحقق بشكل خاص من الوظائف القديمة مع قسم وحدة التخزين الفرعية.

ولقد كان نجاح جيدة مع 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

وعن طريق pexpect [ http://www.noah.org/wiki/Pexpect ] مع عدم عرقلة readlines طريقة أخرى للقيام بذلك. Pexpect يحل المشاكل الجمود، ويسمح لك لتشغيل بسهولة العمليات في الخلفية، ويعطي طرق سهلة لديهم رد عندما تبصق عملية للخروج سلاسل محددة مسبقا، وعموما يجعل التفاعل مع العملية أسهل بكثير.

ولدي نفس المشكلة تحاول الاتصال محطة 3270 باستخدام برنامج s3270 البرمجة في بايثون. أنا الآن حل المشكلة مع فئة فرعية من العملية التي وجدت هنا:

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()

ونظرا "أنا لا تضطر إلى الانتظار لأن يعود"، واحدة من أسهل الحلول أن يكون هذا:

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

ولكن ... من ما قرأت هذه ليست "الطريقة الصحيحة لتحقيق شيء من هذا القبيل" بسبب المخاطر الأمنية التي أنشأتها العلم subprocess.CREATE_NEW_CONSOLE.

والأشياء الرئيسية التي تحدث هنا هو استخدام subprocess.CREATE_NEW_CONSOLE لإنشاء وحدة جديدة و.pid (ID عملية العودة حتى أنك يمكن أن تحقق البرنامج في وقت لاحق إذا كنت تريد) بحيث لا لانتظار البرنامج لإنهاء مهمتها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top