كيف يتم استدعاء أمر خارجي؟

سئل على ١٨ سبتمبر ٢٠٠٨  ·  تمت مشاهدة 3.5M مرة  ·  مصدر

freshWoWer picture
في ١٨ سبتمبر ٢٠٠٨

كيف تستدعي أمرًا خارجيًا (كما لو كنت قد كتبته في غلاف Unix أو موجه أوامر Windows) من داخل نص برمجي Python؟

الإجابات

David Cournapeau picture
في ١٨ سبتمبر ٢٠٠٨
4888

انظر إلى وحدة العملية الفرعية في المكتبة القياسية:

import subprocess
subprocess.run(["ls", "-l"])

ميزة subprocess مقابل system هي أنه أكثر مرونة (يمكنك الحصول على stdout ، stderr ، رمز الحالة "الحقيقي" ، أفضل معالجة الأخطاء ، إلخ ...).

توصي الوثائق الرسمية باستخدام الوحدة النمطية subprocess على البديل os.system() :

توفر الوحدة subprocess تسهيلات أكثر قوة لإنتاج عمليات جديدة واسترداد نتائجها ؛ يفضل استخدام هذه الوحدة عن استخدام هذه الوظيفة [ os.system() ].

قد يحتوي قسم استبدال الوظائف الأقدم بقسم subprocess على بعض الوصفات المفيدة.

لإصدارات Python قبل 3.5 ، استخدم call :

import subprocess
subprocess.call(["ls", "-l"])
Eli Courtwright picture
في ١٨ سبتمبر ٢٠٠٨
3054

فيما يلي ملخص لطرق استدعاء البرامج الخارجية ومزايا وعيوب كل منها:

  1. os.system("some_command with args") بتمرير الأمر والوسيطات إلى قشرة نظامك. هذا جيد لأنه يمكنك بالفعل تشغيل أوامر متعددة مرة واحدة بهذه الطريقة وإعداد الأنابيب وإعادة توجيه الإدخال / الإخراج. فمثلا:

    os.system("some_command < input_file | another_command > output_file")  
    

ومع ذلك ، في حين أن هذا مناسب ، يجب عليك التعامل يدويًا مع هروب أحرف shell مثل المسافات ، إلخ. من ناحية أخرى ، يتيح لك هذا أيضًا تشغيل الأوامر التي هي ببساطة أوامر shell وليست برامج خارجية في الواقع. انظر الوثائق .

  1. سيفعل stream = os.popen("some_command with args") نفس الشيء مثل os.system باستثناء أنه يمنحك كائنًا يشبه الملف يمكنك استخدامه للوصول إلى المدخلات / المخرجات القياسية لهذه العملية. هناك 3 أنواع أخرى من popen تتعامل جميعها مع i / o بشكل مختلف قليلاً. إذا قمت بتمرير كل شيء كسلسلة ، فسيتم تمرير الأمر الخاص بك إلى shell ؛ إذا قمت بتمريرها كقائمة ، فلا داعي للقلق بشأن الهروب من أي شيء. انظر الوثائق .

  2. و Popen الطبقي لل subprocess وحدة. الغرض من هذا هو أن يكون بديلاً عن os.popen ولكن له جانب سلبي يتمثل في كونه أكثر تعقيدًا بعض الشيء بحكم كونه شاملاً للغاية. على سبيل المثال ، قد تقول:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
    

    بدلا من:

    print os.popen("echo Hello World").read()
    

    ولكن من الجيد أن يكون لديك كل الخيارات هناك في فئة موحدة واحدة بدلاً من 4 وظائف مختلفة من popen. انظر الوثائق .

  3. و call ظيفة من subprocess وحدة. هذا يشبه في الأساس فئة Popen ويأخذ جميع الوسائط نفسها ، لكنه ببساطة ينتظر حتى يكتمل الأمر ويعطيك رمز الإرجاع. فمثلا:

    return_code = subprocess.call("echo Hello World", shell=True)  
    

    انظر الوثائق .

  4. إذا كنت تستخدم Python 3.5 أو ما بعده ، فيمكنك استخدام وظيفة subprocess.run ، والتي تشبه إلى حد كبير ما سبق ولكنها أكثر مرونة وتعيد كائن CompletedProcess عندما ينتهي الأمر من التنفيذ.

  5. تحتوي وحدة نظام التشغيل أيضًا على جميع وظائف fork / exec / spawn التي لديك في برنامج C ، لكنني لا أوصي باستخدامها مباشرةً.

من المحتمل أن تكون الوحدة النمطية subprocess هي ما تستخدمه.

أخيرًا ، يرجى الانتباه إلى أنه بالنسبة لجميع العمليات التي تمرر فيها الأمر النهائي ليتم تنفيذه بواسطة shell كسلسلة ، وأنت مسؤول عن الهروب منه. هناك تداعيات أمنية خطيرة إذا كان أي جزء من السلسلة التي تمررها لا يمكن الوثوق به بشكل كامل. على سبيل المثال ، إذا قام المستخدم بإدخال بعض / أي جزء من السلسلة. إذا لم تكن متأكدًا ، فاستخدم هذه الطرق مع الثوابت فقط. لإعطائك تلميحًا عن الآثار المترتبة ، ضع في اعتبارك هذا الرمز:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

وتخيل أن المستخدم أدخل شيئًا "لم تكن والدتي تحبني && rm -rf /" والتي يمكن أن تمحو نظام الملفات بأكمله.

EmmEff picture
في ١٨ سبتمبر ٢٠٠٨
374

التنفيذ النموذجي:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

أنت حر في فعل ما تريد ببيانات stdout في الأنبوب. في الواقع ، يمكنك ببساطة حذف هذه المعلمات ( stdout= و stderr= ) وستتصرف مثل os.system() .

newtover picture
في ١٢ فبراير ٢٠١٠
241

بعض التلميحات حول فصل عملية الطفل عن عملية الاتصال (بدء العملية الفرعية في الخلفية).

لنفترض أنك تريد بدء مهمة طويلة من برنامج نصي CGI. أي أن العملية التابعة يجب أن تعيش لفترة أطول من عملية تنفيذ البرنامج النصي CGI.

المثال الكلاسيكي من وثائق وحدة العملية الفرعية هو:

import subprocess
import sys

# Some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess

# Some more code here

الفكرة هنا هي أنك لا تريد الانتظار في السطر "عملية الاتصال الفرعية" حتى تنتهي longtask.py. ولكن ليس من الواضح ما الذي يحدث بعد السطر "مزيد من التعليمات البرمجية هنا" من المثال.

كان النظام الأساسي الذي استهدفه هو FreeBSD ، لكن التطوير كان على Windows ، لذلك واجهت المشكلة على Windows أولاً.

في نظام التشغيل Windows (Windows XP) ، لن تنتهي العملية الأصلية حتى تنتهي longtask.py من عملها. ليس هذا ما تريده في نص CGI. المشكلة ليست خاصة ببايثون. في مجتمع PHP المشكلات هي نفسها.

الحل هو تمرير DETACHED_PROCESS Process Creation Flag إلى وظيفة CreateProcess الأساسية في Windows API. إذا كنت قد قمت بتثبيت pywin32 ، فيمكنك استيراد العلامة من الوحدة النمطية win32process ، وإلا يجب عليك تحديدها بنفسك:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27eryksun في تعليق أسفل الملاحظات ، أن العلم الصحيح لغويًا هو CREATE_NEW_CONSOLE (0x00000010) * /

في FreeBSD لدينا مشكلة أخرى: عندما تنتهي العملية الأبوية ، فإنها تُنهي العمليات الفرعية أيضًا. وهذا ليس ما تريده أيضًا في برنامج نصي CGI. أظهرت بعض التجارب أن المشكلة بدت في مشاركة النظام. وكان حل العمل كما يلي:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

لم أتحقق من الكود على منصات أخرى ولا أعرف أسباب السلوك على FreeBSD. إذا كان أي شخص يعرف ، يرجى مشاركة أفكارك. لم يسلط البحث في Googling عند بدء عمليات الخلفية في Python أي ضوء حتى الآن.

nimish picture
في ١٨ سبتمبر ٢٠٠٨
163
import os
os.system("your command")

لاحظ أن هذا أمر خطير ، حيث لم يتم تنظيف الأمر. أترك الأمر لك في google للحصول على الوثائق ذات الصلة على الوحدتين النمطية 'os' و 'sys. هناك مجموعة من الوظائف (exec * و spawn *) التي ستقوم بأشياء مماثلة.

sirwart picture
في ١٨ سبتمبر ٢٠٠٨
156

أود أن أوصى باستخدام فرعي أو جانبي وحدة بدلا من os.system لأنه قذيفة الهروب بالنسبة لك، وبالتالي فهو أكثر أمانا.

subprocess.call(['ping', 'localhost'])
Alexandra Franks picture
في ١٨ سبتمبر ٢٠٠٨
150
import os
cmd = 'ls -al'
os.system(cmd)

إذا كنت تريد إرجاع نتائج الأمر ، فيمكنك استخدام os.popen . ومع ذلك ، فقد تم إهمال هذا منذ الإصدار 2.6 لصالح وحدة العملية الفرعية ، والتي غطتها الإجابات الأخرى جيدًا.

Tom Fuller picture
في ٢٩ أكتوبر ٢٠١٦
104

هناك الكثير من المكتبات المختلفة التي تسمح لك باستدعاء أوامر خارجية باستخدام Python. لقد قدمت وصفًا لكل مكتبة وعرضت مثالاً لاستدعاء أمر خارجي. الأمر الذي استخدمته كمثال هو ls -l (سرد كافة الملفات). إذا كنت تريد معرفة المزيد حول أي من المكتبات التي قمت بإدراجها وربطها بالوثائق الخاصة بكل منها.

المصادر:

هذه هي جميع المكتبات:

نأمل أن يساعدك هذا في اتخاذ قرار بشأن المكتبة التي ستستخدمها :)

عملية فرعية

تسمح لك العملية الفرعية باستدعاء الأوامر الخارجية وتوصيلها بأنابيب الإدخال / الإخراج / الخطأ (stdin و stdout و stderr). العملية الفرعية هي الخيار الافتراضي لتشغيل الأوامر ، ولكن في بعض الأحيان تكون الوحدات النمطية الأخرى أفضل.

subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command

نظام التشغيل

يستخدم نظام التشغيل "للوظائف المعتمدة على نظام التشغيل". يمكن استخدامه أيضًا لاستدعاء الأوامر الخارجية بـ os.system و os.popen (ملاحظة: يوجد أيضًا subprocess.popen). سيقوم نظام التشغيل دائمًا بتشغيل shell وهو بديل بسيط للأشخاص الذين لا يحتاجون إلى ذلك ، أو لا يعرفون كيفية استخدام subprocess.run .

os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output

ش

sh هي واجهة عملية فرعية تتيح لك استدعاء البرامج كما لو كانت وظائف. هذا مفيد إذا كنت تريد تشغيل أمر عدة مرات.

sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function

البرقوق

plumbum هي مكتبة لبرامج Python "الشبيهة بالنصوص". يمكنك استدعاء برامج مثل الوظائف كما في sh . يعتبر Plumbum مفيدًا إذا كنت تريد تشغيل خط أنابيب بدون الغلاف.

ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command

توقع

يتيح لك pexpect نشر التطبيقات الفرعية والتحكم فيها والعثور على أنماط في إخراجها. هذا بديل أفضل للعملية الفرعية للأوامر التي تتوقع tty على يونكس.

pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo [email protected]:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

قماش

النسيج هو مكتبة Python 2.5 و 2.7. يسمح لك بتنفيذ أوامر shell المحلية والبعيدة. النسيج هو بديل بسيط لتشغيل الأوامر في غلاف آمن (SSH)

fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output

مبعوث

مبعوث يعرف باسم "عملية فرعية للبشر". يتم استخدامه كغلاف ملائم حول الوحدة النمطية subprocess .

r = envoy.run("ls -l") # Run command
r.std_out # get output

أوامر

commands على وظائف غلاف لـ os.popen ، لكن تمت إزالته من Python 3 لأن subprocess هو بديل أفضل.

اعتمد التعديل على تعليق جي إف سيباستيان.

Jorge E. Cardona picture
في ١٣ مارس ٢٠١٢
78

أستخدم دائمًا fabric لهذه الأشياء مثل:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

ولكن يبدو أن هذه أداة جيدة: sh (واجهة عملية فرعية لبايثون) .

انظر إلى مثال:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)
Honza Javorek picture
في ١١ أبريل ٢٠١٣
77

مع المكتبة القياسية

استخدم وحدة العملية الفرعية (Python 3):

import subprocess
subprocess.run(['ls', '-l'])

إنها الطريقة القياسية الموصى بها. ومع ذلك ، يمكن أن تكون المهام الأكثر تعقيدًا (الأنابيب ، والمخرجات ، والمدخلات ، وما إلى ذلك) مملة في الإنشاء والكتابة.

ملاحظة حول إصدار Python: إذا كنت لا تزال تستخدم Python 2 ، فإن subprocess.call يعمل بطريقة مماثلة.

ProTip: يمكن أن يساعدك shlex.split في تحليل الأمر مقابل run و call subprocess ووظائف أخرى

import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))

مع التبعيات الخارجية

إذا كنت لا تمانع في التبعيات الخارجية ، فاستخدم plumbum :

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

إنه أفضل غلاف subprocess . إنه متعدد المنصات ، أي أنه يعمل على كل من أنظمة Windows و Unix. التثبيت بواسطة pip install plumbum .

مكتبة شعبية أخرى هي sh :

from sh import ifconfig
print(ifconfig('wlan0'))

ومع ذلك ، فقد انخفض دعم Windows sh ، لذلك لم يكن رائعًا كما كان من قبل. التثبيت بواسطة pip install sh .