โปรเซสย่อยใน Python คืออะไร? [5 ตัวอย่างการใช้งาน]
เผยแพร่แล้ว: 2021-05-23กระบวนการย่อยช่วยให้คุณโต้ตอบกับระบบปฏิบัติการในระดับใหม่โดยสิ้นเชิง
คอมพิวเตอร์ของเรารันกระบวนการย่อยตลอดเวลา อันที่จริง เพียงแค่อ่านบทความนี้ คุณกำลังใช้งานกระบวนการมากมาย เช่น ตัวจัดการเครือข่าย หรือตัวอินเทอร์เน็ตเบราว์เซอร์เอง
สิ่งที่ยอดเยี่ยมเกี่ยวกับสิ่งนี้คือการดำเนินการใดๆ ที่เราทำบนคอมพิวเตอร์ของเรานั้นเกี่ยวข้องกับการเรียกใช้กระบวนการย่อย นั่นยังคงเป็นจริงแม้ว่าเราจะเขียนสคริปต์ "สวัสดีชาวโลก" แบบง่าย ๆ ในไพ ธ อน
แนวคิดของ กระบวนการย่อย อาจดูคลุมเครือแม้ว่าคุณจะเรียนรู้การเขียนโปรแกรมมาระยะหนึ่งแล้ว บทความนี้จะกล่าวถึงแนวคิดหลักของกระบวนการย่อยอย่างละเอียด และวิธีใช้ไลบรารีมาตรฐานของกระบวนการย่อย Python
ในตอนท้ายของบทช่วยสอนนี้ คุณจะ:
- เข้าใจแนวคิดของกระบวนการย่อย
- ได้เรียนรู้พื้นฐานของไลบรารีกระบวนการย่อยของ Python
- ฝึกฝนทักษะ Python ของคุณด้วยตัวอย่างที่เป็นประโยชน์
เข้าเรื่องกันเลย
แนวคิดของกระบวนการย่อย
กล่าวอย่างกว้างๆ กระบวนการย่อยคือกระบวนการทางคอมพิวเตอร์ที่สร้างขึ้นโดยกระบวนการอื่น
เราสามารถนึกถึงโปรเซสย่อยเป็นทรี ซึ่งโปรเซสพาเรนต์แต่ละโปรเซสมีโปรเซสย่อยทำงานอยู่เบื้องหลัง ฉันรู้ว่าสิ่งนี้อาจทำให้สับสนได้ แต่มาดูด้วยกราฟิคง่ายๆ กัน

มีหลายวิธีที่เราจะแสดงภาพกระบวนการที่ทำงานบนคอมพิวเตอร์ของเราได้ ตัวอย่างเช่น ใน UNIX (Linux & MAC) เรามี htop ซึ่งเป็นโปรแกรมดูกระบวนการแบบโต้ตอบ

โหมดทรี เป็นเครื่องมือที่มีประโยชน์ที่สุดในการดูกระบวนการย่อยที่ทำงานอยู่ เราสามารถเปิดใช้งานได้ด้วย F5 .
หากเราพิจารณาส่วนคำสั่งอย่างละเอียด เราจะสังเกตเห็นโครงสร้างของกระบวนการที่ทำงานบนคอมพิวเตอร์ของเรา

ทุกอย่างเริ่มต้นด้วย /sbin/init ซึ่งเป็นคำสั่งที่เริ่มต้นแต่ละกระบวนการบนคอมพิวเตอร์ของเรา จากจุดนั้น เราจะเห็นจุดเริ่มต้นของกระบวนการอื่นๆ เช่น xfce4-screenshoter และ xfce4-terminal (ซึ่งนำไปสู่กระบวนการย่อยมากยิ่งขึ้น)
เมื่อดูที่ Windows เรามีตัวจัดการงานในตำนานซึ่งให้ผลลัพธ์ที่เป็นประโยชน์เมื่อฆ่าโปรแกรมที่หยุดทำงานเหล่านั้นในเครื่องของเรา

ตอนนี้เรามีแนวคิดที่ชัดเจน มาดูกันว่าเราจะใช้กระบวนการย่อยใน Python ได้อย่างไร
กระบวนการย่อยใน Python
กระบวนการย่อยใน Python เป็นงานที่สคริปต์หลามมอบหมายให้กับระบบปฏิบัติการ (OS)
ไลบรารีกระบวนการย่อยช่วยให้เราสามารถดำเนินการและจัดการกระบวนการย่อยได้โดยตรงจาก Python ที่เกี่ยวข้องกับการทำงานกับอินพุตมาตรฐาน stdin เอาต์พุตมาตรฐาน stdout และโค้ดส่งคืน
เราไม่ต้องติดตั้งด้วย PIP เนื่องจากเป็นส่วนหนึ่งของไลบรารีมาตรฐาน Python
ดังนั้น เราสามารถเริ่มใช้กระบวนการย่อยใน python ได้โดยการนำเข้าโมดูล
import subprocess # Using the module ....หมายเหตุ: หากต้องการติดตามบทความนี้ คุณควรมี Python 3.5 +
หากต้องการตรวจสอบเวอร์ชันหลามที่คุณมี ให้เรียกใช้
❯ 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 ซึ่งเป็นทางลัดที่เป็นมิตรกับกระบวนการย่อย Popen ฟังก์ชัน 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)เรากำลังรันคำสั่งนี้เป็นสตริงและใช้อาร์กิวเมนต์ เชลล์ นั่นหมายความว่าเรากำลังเรียกใช้เชลล์เมื่อเริ่มต้นการประมวลผลย่อยของเรา และเชลล์ตีความอาร์กิวเมนต์คำสั่งโดยตรง
อย่างไรก็ตาม use shell=True มีข้อเสียมากมาย และที่แย่ที่สุดคือการรั่วไหลของความปลอดภัย คุณสามารถอ่านเกี่ยวกับพวกเขาในเอกสารอย่างเป็นทางการ
วิธีที่ดีที่สุดในการส่งคำสั่งไปยังฟังก์ชัน run คือการใช้รายการที่ 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สมบูรณ์แบบ ตอนนี้เรารู้พื้นฐานของไลบรารี กระบวนการย่อย แล้ว ก็ถึงเวลาไปที่ตัวอย่างการใช้งาน
ตัวอย่างการใช้งานของ subprocess ใน Python
ในส่วนนี้ เราจะทบทวนการใช้งานจริงบางประการของไลบรารีกระบวนการย่อย คุณสามารถตรวจสอบทั้งหมดได้ในที่เก็บ Github นี้
ตัวตรวจสอบโปรแกรม
การใช้งานหลักอย่างหนึ่งของไลบรารีนี้คือความสามารถในการใช้งานระบบปฏิบัติการอย่างง่าย
ตัวอย่างเช่น สคริปต์ง่าย ๆ ที่ตรวจสอบว่ามีการติดตั้งโปรแกรมหรือไม่ ใน Linux เราสามารถทำได้ด้วยคำสั่ง which
'''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 ที่รับรองว่าสตริงของโปรแกรมประกอบด้วยตัวอักษรและตัวเลขเท่านั้น เราตรวจสอบการมีอยู่ของแต่ละโปรแกรมด้วย a for a loop
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" (หมายถึงการนับ) และกำหนดผลลัพธ์ของการจับคู่ด้วยเงื่อนไข
หากคุณเรียกใช้ไฟล์นี้ (จำไว้ว่าคุณสามารถดาวน์โหลดไฟล์ข้อความจาก repo Github)
ตั้งค่า virtualenv ด้วยกระบวนการย่อย
หนึ่งในสิ่งที่ยอดเยี่ยมที่สุดที่คุณสามารถทำได้ด้วย Python คือกระบวนการอัตโนมัติ สคริปต์ประเภทนี้สามารถช่วยคุณประหยัดเวลาได้หลายชั่วโมงต่อสัปดาห์
ตัวอย่างเช่น เราจะสร้างสคริปต์การตั้งค่าที่สร้างสภาพแวดล้อมเสมือนสำหรับเรา และพยายามค้นหาไฟล์ ข้อกำหนด. 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 ...")ในกรณีนี้ เรากำลังใช้หลายกระบวนการและแยกวิเคราะห์ข้อมูลที่เราต้องการในสคริปต์หลามของเรา เรายังใช้ไลบรารี 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
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 one-liner แบบธรรมดา แต่นี่เป็นเพียงเพื่อการทดสอบเท่านั้น
เราจะสร้างสคริปต์ 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++ ขนาดใหญ่ เนื่องจากเรากำลังโหลดเอาต์พุตในหน่วยความจำและอาจทำให้หน่วยความจำรั่วได้
เปิดโปรแกรมภายนอก
เราสามารถเรียกใช้โปรแกรมอื่นได้เพียงแค่เรียกตำแหน่งไบนารีของโปรแกรมเหล่านั้นผ่านกระบวนการย่อย
มาลองใช้โดยเปิด Brave เว็บเบราว์เซอร์ที่ฉันชอบ
import subprocess subprocess.run('brave')การดำเนินการนี้จะเปิดอินสแตนซ์ของเบราว์เซอร์ หรือเพียงแค่แท็บอื่นหากคุณใช้งานเบราว์เซอร์อยู่แล้ว

เช่นเดียวกับโปรแกรมอื่นๆ ที่ยอมรับแฟล็ก เราสามารถใช้พวกมันเพื่อสร้างพฤติกรรมที่ต้องการได้
import subprocess subprocess.run(['brave', '--incognito']) 
สรุป
กระบวนการย่อยคือกระบวนการทางคอมพิวเตอร์ที่สร้างขึ้นโดยกระบวนการอื่น เราสามารถตรวจสอบกระบวนการที่คอมพิวเตอร์ของเราใช้งานด้วยเครื่องมือต่างๆ เช่น htop และตัวจัดการงาน
Python มีห้องสมุดของตัวเองเพื่อทำงานกับกระบวนการย่อย ในปัจจุบัน ฟังก์ชัน เรียกใช้ ช่วยให้เรามีอินเทอร์เฟซง่ายๆ ในการสร้างและจัดการกระบวนการย่อย
เราสามารถสร้างแอปพลิเคชันประเภทใดก็ได้กับพวกเขา เนื่องจากเรากำลังโต้ตอบกับระบบปฏิบัติการโดยตรง
สุดท้าย จำไว้ว่าวิธีที่ดีที่สุดในการเรียนรู้คือการสร้างสิ่งที่คุณต้องการใช้
