1. ما هو وحدة subprocess في بايثون
نظرة عامة
وحدة subprocess
في بايثون هي أداة قوية لتنفيذ أوامر النظام أو البرامج الخارجية من خلال بايثون. باستخدام هذه الوحدة، يمكنك إدارة عمليات الإدخال والإخراج القياسية والتحكم في العمليات، مما يسهل التكامل بين برامج بايثون والبرامج الخارجية. توفر طريقة أكثر أمانًا ومرونة للتحكم في العمليات مقارنةً باستخدام os.system()
أو وحدة commands
القديمة.
الاستخدامات الرئيسية
- تنفيذ أوامر الشيل: استدعاء أوامر النظام البسيطة.
- إدارة العمليات: تشغيل البرامج الخارجية وإعادة توجيه الإدخال والإخراج القياسي.
- المعالجة غير المتزامنة: إدارة المهام الطويلة أو المهام المتوازية.
2. الاستخدام الأساسي: subprocess.run()
كيفية الاستخدام الأساسي
دالة subprocess.run()
تتيح تنفيذ أوامر النظام بسهولة من داخل بايثون. على سبيل المثال، لعرض قائمة الملفات في مجلد، يمكنك استخدام الكود التالي:
import subprocess
result = subprocess.run(['ls', '-l'], capture_output=True, text=True)
print(result.stdout)
يقوم هذا الكود بتنفيذ الأمر ls -l
وتخزين الناتج في stdout
ليتم معالجته بواسطة بايثون. بفضل capture_output=True
، يتم التقاط الناتج القياسي، ومع text=True
يتم التعامل مع النتائج كنص.
معالجة الأخطاء
باستخدام subprocess.run()
، إذا فشل الأمر يمكنك الحصول على رسالة الخطأ عبر stderr
والتحقق من حالة التنفيذ عبر returncode
.
result = subprocess.run(['ls', 'nonexistentfile'], capture_output=True, text=True)
if result.returncode != 0:
print(f"خطأ: {result.stderr}")
في هذا المثال، إذا تم تحديد ملف غير موجود، فسيتم عرض رسالة الخطأ من المخرجات القياسية للأخطاء.
3. التنفيذ غير المتزامن: subprocess.Popen()
المعالجة غير المتزامنة باستخدام Popen
بما أن subprocess.run()
يعالج الأوامر بشكل متزامن، فإن برنامج بايثون ينتظر انتهاء الأمر قبل الانتقال للخطوة التالية. لكن باستخدام subprocess.Popen()
، يمكنك تنفيذ العمليات بشكل غير متزامن والسماح بمتابعة العمليات الأخرى في نفس الوقت.
import subprocess
proc = subprocess.Popen(['sleep', '5'], stdout=subprocess.PIPE)
print("تم بدء العملية")
proc.wait()
print("تم الانتهاء من العملية")
يقوم هذا الكود بتنفيذ sleep 5
بشكل غير متزامن، ويمكنك متابعة إجراءات أخرى أثناء تنفيذ العملية.
التحكم في الإدخال والإخراج القياسي
باستخدام Popen
، يمكنك إعادة توجيه الإدخال والإخراج القياسي بشكل دقيق. على سبيل المثال، يقوم الكود التالي بقراءة البيانات من ملف ومعالجتها باستخدام أمر cat
ثم كتابتها في ملف آخر:
with open('input.txt', 'r') as infile, open('output.txt', 'w') as outfile:
proc = subprocess.Popen(['cat'], stdin=infile, stdout=outfile)
proc.wait()
بهذه الطريقة، يمكنك إعادة توجيه الإدخال والإخراج للبرامج الخارجية مباشرة إلى ومن الملفات.
4. أمثلة الاستخدام: سكريبتات الأتمتة
نسخ احتياطي للملفات
وحدة subprocess
مفيدة جدًا في أتمتة مهام إدارة النظام أو تنفيذ مهام دورية. مثل المثال التالي الذي ينسخ الملفات تلقائيًا إلى مجلد نسخ احتياطي:
import subprocess
files_to_backup = ['file1.txt', 'file2.txt', 'file3.txt']
backup_dir = '/backup/directory/'
for file in files_to_backup:
subprocess.run(['cp', file, backup_dir])
يقوم هذا الكود بنسخ الملفات المحددة إلى مجلد النسخ الاحتياطي، ما يسمح بأتمتة المهام الدورية بسهولة.
الاستخدام في خطوط CI/CD
تُستخدم subprocess
أيضًا في بيئات التكامل المستمر (CI) والتسليم المستمر (CD)، لتشغيل سكريبتات الاختبار أو عمليات النشر تلقائيًا. يمكنك تنفيذ سكريبتات الاختبار والانتقال للخطوات التالية بناءً على النتائج.
5. الأمان وأفضل الممارسات
مخاطر shell=True
خيار shell=True
يُستخدم عند تنفيذ الأوامر عبر الشيل، لكنه يحمل مخاطر أمنية خصوصًا عند تمرير مدخلات خارجية مباشرة، حيث قد يتعرض البرنامج لهجمات “حقن الشيل”. يفضل استخدام shell=False
للحد من هذه المخاطر.
import subprocess
# الطريقة الموصى بها (آمنة)
subprocess.run(['ls', '-l'])
# shell=True (بحذر)
subprocess.run('ls -l', shell=True)
التوافق مع الأنظمة المختلفة
تختلف أوامر النظام بين أنظمة التشغيل. يمكنك استخدام وحدة platform
في بايثون لتحديد النظام وتغيير الأمر المنفذ بناءً على ذلك.
import platform
import subprocess
if platform.system() == "Windows":
subprocess.run(['dir'], shell=True)
else:
subprocess.run(['ls', '-l'])
6. استكشاف الأخطاء وتصحيحها
أخطاء شائعة وحلولها
عند استخدام subprocess
، قد تواجه أخطاء مثل “عدم العثور على الملف” أو “عدم وجود صلاحية”. يمكنك التقاط هذه الأخطاء عبر stderr
والتحقق من returncode
لمعرفة التفاصيل.
نصائح لتصحيح الأخطاء
استخدام خيار check=True
يجعل بايثون يرفع استثناء عند فشل الأمر، مما يساعد في اكتشاف المشكلات مبكرًا. يمكنك أيضًا التقاط الإخراج القياسي ورسائل الخطأ وتسجيلها لتسهيل عملية التصحيح.
import subprocess
try:
result = subprocess.run(['ls', '-l'], check=True, capture_output=True, text=True)
print(result.stdout)
except subprocess.CalledProcessError as e:
print(f"حدث خطأ: {e}")
7. العمليات غير المتزامنة مع asyncio
استخدام asyncio للمعالجة غير المتزامنة
باستخدام asyncio
مع subprocess
، يمكنك تنفيذ عدة عمليات في نفس الوقت بشكل غير متزامن. المثال التالي ينفذ أمر ls
بشكل غير متزامن ويعرض النتائج:
import asyncio
import subprocess
async def run_command():
proc = await asyncio.create_subprocess_exec('ls', '-l',
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
stdout, stderr = await proc.communicate()
if stdout:
print(f'[stdout]n{stdout.decode()}')
if stderr:
print(f'[stderr]n{stderr.decode()}')
asyncio.run(run_command())
يقوم هذا الكود بتنفيذ الأمر بشكل غير متزامن ومعالجة نتائج الإخراج والأخطاء بكفاءة باستخدام asyncio
.