ما هي العملية الفرعية في بايثون؟ [5 أمثلة على الاستخدام]

نشرت: 2021-05-23

تتيح لك العمليات الفرعية التفاعل على مستوى جديد تمامًا مع نظام التشغيل.

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

الشيء الرائع في هذا الأمر هو أن أي إجراء نقوم به على جهاز الكمبيوتر الخاص بنا يتضمن استدعاء عملية فرعية. يظل هذا صحيحًا حتى لو كنا نكتب نصًا بسيطًا "hello world" بلغة Python.

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

بنهاية هذا البرنامج التعليمي ، سوف:

  • فهم مفهوم العملية الفرعية
  • تعلمت أساسيات مكتبة العمليات الفرعية في بايثون
  • تدرب على مهاراتك في بايثون مع أمثلة مفيدة

دعنا ندخله

مفهوم العملية الفرعية

بشكل عام ، العملية الفرعية هي عملية كمبيوتر تم إنشاؤها بواسطة عملية أخرى.

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

هناك عدة طرق يمكننا من خلالها تصور العملية الجارية على جهاز الكمبيوتر الخاص بنا. على سبيل المثال ، في UNIX (Linux و MAC) لدينا htop ، وهو عارض عملية تفاعلي.

عارض عملية Htop

يعد وضع الشجرة الأداة الأكثر فائدة لإلقاء نظرة على العمليات الفرعية الجارية. يمكننا تفعيله مع F5 .

إذا ألقينا نظرة فاحصة على قسم الأوامر ، يمكننا أن نلاحظ بنية العمليات التي تعمل على جهاز الكمبيوتر الخاص بنا.

هيكل عملية Htop
يبدأ كل شيء بـ / sbin / init وهو الأمر الذي يبدأ كل عملية على جهاز الكمبيوتر الخاص بنا. من هذه النقطة ، يمكننا أن نرى بداية عمليات أخرى مثل xfce4-screenshoter و xfce4-terminal (مما يؤدي إلى مزيد من العمليات الفرعية )

بإلقاء نظرة على Windows ، لدينا مدير المهام الأسطوري الذي ينتج عنه فائدة عند قتل تلك البرامج المعطلة على أجهزتنا.

مدير مهام الويندوز

الآن لدينا مفهوم واضح وضوح الشمس. دعونا نرى كيف يمكننا تنفيذ العمليات الفرعية في بايثون.

العمليات الفرعية في بايثون

العملية الفرعية في Python هي مهمة يفوضها نص Python إلى نظام التشغيل (OS).

تتيح لنا مكتبة العمليات الفرعية تنفيذ العمليات الفرعية وإدارتها مباشرةً من Python. يتضمن ذلك العمل مع stdin الإدخال القياسي و stdout الإخراج القياسي ورموز الإرجاع.

لا يتعين علينا تثبيته باستخدام PIP ، لأنه جزء من مكتبة Python القياسية.

لذلك يمكننا البدء في استخدام العمليات الفرعية في بايثون فقط عن طريق استيراد الوحدة.

 import subprocess # Using the module ....

ملاحظة: لمتابعة هذا المقال ، يجب أن يكون لديك Python 3.5 +

للتحقق من إصدار python الذي لديك حاليًا ، ما عليك سوى تشغيل.

 ❯ python --version Python 3.9.5 # My result

في حال كان إصدار Python الذي تحصل عليه هو 2.x ، يمكنك استخدام الأمر التالي

 python3 --version

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

هذا يعني أنه يمكننا فعل ما نريد ، طالما أن نظام التشغيل الخاص بنا يسمح لنا (وطالما أنك لم تقم بإزالة نظام ملفات الجذر الخاص بك).

دعونا نرى كيفية استخدامه عن طريق إنشاء برنامج نصي بسيط يسرد ملفات الدليل الحالي.

أول تطبيق للعملية الفرعية

أولاً ، لنقم بإنشاء ملف list_dir.py . سيكون هذا هو الملف حيث سنقوم بتجربة ملفات القوائم.

 touch list_dir.py

الآن دعنا نفتح هذا الملف ونستخدم الكود التالي.

 import subprocess subprocess.run('ls')

أولا، نحن استيراد وحدة فرعي أو جانبي، ثم استخدام على المدى ظيفة التي تدير، الأمر نعبر كحجة.

تم تقديم هذه الوظيفة في Python 3.5 ، كاختصار سهل للعملية الفرعية. تسمح لنا وظيفة subprocess.run بتشغيل أمر والانتظار حتى ينتهي ، على عكس Popen حيث لدينا خيار الاتصال بالتواصل لاحقًا.

بالحديث عن إخراج الكود ، فإن ls هو أمر UNIX يسرد ملفات الدليل الذي تتواجد فيه. لذلك ، إذا قمت بتشغيل هذا الأمر ، فستحصل على قائمة بالملفات الموجودة في الدليل الحالي.

 ❯ python list_dir.py example.py LICENSE list_dir.py README.md

ملاحظة: ضع في اعتبارك أنه إذا كنت تستخدم نظام Windows ، فستحتاج إلى استخدام أوامر مختلفة. على سبيل المثال بدلاً من استخدام "ls" يمكنك استخدام "dir"

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

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

 import subprocess # subprocess.run('ls') # Simple command subprocess.run('ls -la', shell=True)

نقوم بتشغيل هذا الأمر كسلسلة نصية ونستخدم الصدفة الوسيطة. هذا يعني أننا نستدعي الصدفة في بداية تنفيذ عمليتنا الفرعية ، ويتم تفسير وسيطة الأمر مباشرة بواسطة الصدفة.

ومع ذلك ، فإن استخدام shell = True له العديد من الجوانب السلبية ، والأسوأ هو التسريبات الأمنية المحتملة. يمكنك أن تقرأ عنها في الوثائق الرسمية.

أفضل طريقة لتمرير الأوامر إلى دالة التشغيل هي استخدام قائمة حيث يكون lst [0] هو الأمر المطلوب استدعاء (ls في هذه الحالة) و lst [n] هما وسيطات هذا الأمر.

إذا فعلنا ذلك ، سيبدو كودنا هكذا.

 import subprocess # subprocess.run('ls') # Simple command # subprocess.run('ls -la', shell=True) # Dangerous command subprocess.run(['ls', '-la'])

إذا أردنا تخزين المخرجات القياسية لعملية فرعية في متغير ، فيمكننا القيام بذلك عن طريق تعيين معامل capture_output إلى صحيح.

 list_of_files = subprocess.run(['ls', '-la'], capture_output=True) print(list_of_files.stdout) ❯ python list_dir.py b'total 36\ndrwxr-xr-x 3 daniel daniel 4096 may 20 21:08 .\ndrwx------ 30 daniel daniel 4096 may 20 18:03 ..\n-rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.py\ndrwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .git\n-rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignore\n-rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.py\n-rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSE\n-rw-r--r-- 1 daniel daniel 216 may 20 22:12 list_dir.py\n-rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.md\n'

للوصول إلى ناتج العملية ، نستخدم سمة المثيل stdout .

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

 list_of_files = subprocess.run(['ls', '-la'], capture_output=True, text=True) print(list_of_files.stdout) ❯ python list_dir.py total 36 drwxr-xr-x 3 daniel daniel 4096 may 20 21:08 . drwx------ 30 daniel daniel 4096 may 20 18:03 .. -rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.py drwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .git -rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignore -rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.py -rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSE -rw-r--r-- 1 daniel daniel 227 may 20 22:14 list_dir.py -rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.md

ممتاز ، الآن بعد أن عرفنا أساسيات مكتبة العمليات الفرعية ، حان الوقت للانتقال إلى بعض أمثلة الاستخدام.

أمثلة على استخدام العملية الفرعية في بايثون

في هذا القسم ، سنراجع بعض الاستخدامات العملية لمكتبة العمليات الفرعية. يمكنك التحقق منهم جميعًا في مستودع Github هذا.

مدقق البرنامج

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

على سبيل المثال ، نص بسيط يتحقق من تثبيت البرنامج. في Linux ، يمكننا القيام بذلك باستخدام الأمر الذي .

 '''Program checker with subprocess''' import subprocess program = 'git' process = subprocess. run(['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'The program "{program}" is installed') print(f'The location of the binary is: {process.stdout}') else: print(f'Sorry the {program} is not installed') print(process.stderr)

ملاحظة: في نظام UNIX عندما يكون الأمر ناجحًا ، يكون رمز الحالة الخاص به هو 0. وإلا حدث خطأ ما أثناء التنفيذ

نظرًا لأننا لا نستخدم الوسيطة shell = True ، فيمكننا أخذ مدخلات المستخدم بأمان. أيضًا ، يمكننا التحقق مما إذا كان الإدخال برنامجًا صالحًا بنمط regex.

 import subprocess import re programs = input('Separe the programs with a space: ').split() secure_pattern = '[\w\d]' for program in programs: if not re.match(secure_pattern, program): print("Sorry we can't check that program") continue process = subprocess. run( ['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'The program "{program}" is installed') print(f'The location of the binary is: {process.stdout}') else: print(f'Sorry the {program} is not installed') print(process.stderr) print('\n')

في هذه الحالة ، نحصل على البرامج من المستخدم ونستخدم تعبير regex يصادق على أن سلسلة البرنامج تتضمن أحرفًا وأرقامًا فقط. نتحقق من وجود كل برنامج بحلقة for a.

Grep بسيط في Python

لدى صديقك توم قائمة بالأنماط في ملف نصي وملف كبير آخر يريد فيه الحصول على عدد التطابقات لكل نمط. كان يقضي ساعات في تشغيل أمر grep لكل نمط.

لحسن الحظ ، تعرف كيفية حل هذه المشكلة مع Python ، وستساعده على إنجاز هذه المهمة في ثوانٍ قليلة.

 import subprocess patterns_file = 'patterns.txt' readfile = 'romeo-full.txt' with open(patterns_file, 'r') as f: for pattern in f: pattern = pattern.strip() process = subprocess.run( ['grep', '-c', f'{pattern}', readfile], capture_output=True, text=True) if int(process.stdout) == 0: print( f'The pattern "{pattern}" did not match any line of {readfile}') continue print(f'The pattern "{pattern}" matched {process.stdout.strip()} times')

بإلقاء نظرة على هذا الملف ، نحدد متغيرين هما أسماء الملفات التي نريد العمل معها. ثم نفتح الملف الذي يحتوي على جميع الأنماط ونكررها. بعد ذلك ، نسمي عملية فرعية تقوم بتشغيل أمر grep بعلامة "-c" (تعني العد) ونحدد ناتج التطابق بعلامة شرطية.

إذا قمت بتشغيل هذا الملف (تذكر أنه يمكنك تنزيل الملفات النصية من Github repo)

قم بإعداد Virtualenv مع عملية فرعية

من أروع الأشياء التي يمكنك القيام بها باستخدام Python أتمتة العمليات. يمكن أن يوفر لك هذا النوع من النصوص ساعات من الوقت في الأسبوع.

على سبيل المثال ، سنقوم بإنشاء برنامج نصي للإعداد يقوم بإنشاء بيئة افتراضية لنا ويحاول العثور على ملف requirements.txt في الدليل الحالي لتثبيت جميع التبعيات.

 import subprocess from pathlib import Path VENV_NAME = '.venv' REQUIREMENTS = 'requirements.txt' process1 = subprocess.run(['which', 'python3'], capture_output=True, text=True) if process1.returncode != 0: raise OSError('Sorry python3 is not installed') python_bin = process1.stdout.strip() print(f'Python found in: {python_bin}') process2 = subprocess.run('echo "$SHELL"', shell=True, capture_output=True, text=True) shell_bin = process2.stdout.split('/')[-1] create_venv = subprocess.run([python_bin, '-m', 'venv', VENV_NAME], check=True) if create_venv.returncode == 0: print(f'Your venv {VENV_NAME} has been created') pip_bin = f'{VENV_NAME}/bin/pip3' if Path(REQUIREMENTS).exists(): print(f'Requirements file "{REQUIREMENTS}" found') print('Installing requirements') subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS]) print('Process completed! Now activate your environment with "source .venv/bin/activate"') else: print("No requirements specified ...")

في هذه الحالة ، نستخدم عمليات متعددة ونحلل البيانات التي نحتاجها في نص Python الخاص بنا. نحن نستخدم أيضًا مكتبة pathlib التي تتيح لنا معرفة ما إذا كان ملف requirements.txt موجودًا.

إذا قمت بتشغيل ملف python ، فستتلقى بعض الرسائل المفيدة لما يحدث مع نظام التشغيل.

 ❯ python setup.py Python found in: /usr/bin/python3 Your venv .venv has been created Requirements file "requirements.txt" found Installing requirements Collecting asgiref==3.3.4 ....... Process completed! Now activate your environment with "source .venv/bin/activate"

لاحظ أننا نحصل على الإخراج من عملية التثبيت لأننا لا نعيد توجيه الإخراج القياسي إلى متغير.

قم بتشغيل لغة برمجة أخرى

يمكننا تشغيل لغات برمجة أخرى باستخدام python والحصول على الإخراج من تلك الملفات. هذا ممكن لأن العمليات الفرعية تتفاعل مباشرة مع نظام المنطوق.

على سبيل المثال ، لنقم بإنشاء برنامج hello world باللغتين C ++ و Java. من أجل تنفيذ الملف التالي ، ستحتاج إلى تثبيت C ++ و Java compilers.

helloworld.cpp

 #include <iostream> int main(){ std::cout << "This is a hello world in C++" << std::endl; return 0; }


helloworld.java

 class HelloWorld{ public static void main(String args[]){ System.out.println("This is a hello world in Java"); } }


أعلم أن هذا يبدو كثيرًا من التعليمات البرمجية مقارنةً بسطر واحد بسيط من Python ، ولكن هذا مخصص فقط لأغراض الاختبار.

سنقوم بإنشاء برنامج نصي بلغة Python يقوم بتشغيل جميع ملفات C ++ و Java في دليل ما. للقيام بذلك أولاً ، نريد الحصول على قائمة بالملفات اعتمادًا على امتداد الملف ، ويتيح لنا glob القيام بذلك بسهولة!

 from glob import glob # Gets files with each extension java_files = glob('*.java') cpp_files = glob('*.cpp')

بعد ذلك ، يمكننا البدء في استخدام العمليات الفرعية لتنفيذ كل نوع من أنواع الملفات.

 for file in cpp_files: process = subprocess.run(f'g++ {file} -o out; ./out', shell=True, capture_output=True, text=True) output = process.stdout.strip() + ' BTW this was runned by Python' print(output) for file in java_files: without_ext = file.strip('.java') process = subprocess.run(f'java {file}; java {without_ext}',shell=True, capture_output=True, text=True) output = process.stdout.strip() + ' A Python subprocess runned this :)' print(output)

إحدى الحيل الصغيرة هي استخدام شريط وظيفة السلسلة لتعديل الإخراج والحصول على ما نحتاجه فقط.

ملاحظة: كن حذرًا عند تشغيل ملفات Java أو C ++ كبيرة نظرًا لأننا نقوم بتحميل مخرجاتها في الذاكرة وقد يؤدي ذلك إلى حدوث تسرب للذاكرة.

افتح البرامج الخارجية

نحن قادرون على تشغيل برامج أخرى فقط عن طريق استدعاء موقع الثنائيات الخاصة بهم من خلال عملية فرعية.

دعونا نحاول من خلال فتح الشجعان، يا متصفح الويب المفضل.

 import subprocess subprocess.run('brave')

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

متصفح مفتوح

كما هو الحال مع أي برنامج آخر يقبل العلامات يمكننا استخدامها لإنتاج السلوك المطلوب.

 import subprocess subprocess.run(['brave', '--incognito'])

علم التخفي

لنلخص

العملية الفرعية هي عملية كمبيوتر تم إنشاؤها بواسطة عملية أخرى. يمكننا التحقق من العمليات التي يعمل بها جهاز الكمبيوتر الخاص بنا باستخدام أدوات مثل htop ومدير المهام.

تمتلك Python مكتبتها الخاصة للعمل مع العمليات الفرعية. حاليًا ، تمنحنا وظيفة التشغيل واجهة بسيطة لإنشاء العمليات الفرعية وإدارتها.

يمكننا إنشاء أي نوع من التطبيقات معهم لأننا نتفاعل مباشرة مع نظام التشغيل.

أخيرًا ، تذكر أن أفضل طريقة للتعلم هي إنشاء شيء ترغب في استخدامه.