Bạn có thể dùng mô-đun subprocess của Python để tạo tiến trình mới, kết nối với đầu vào/đầu ra của chúng và lấy mã trả về và/hoặc đầu ra của tiến trình. Trong bài viết này, chúng ta sẽ tìm hiểu những điều cơ bản về mô-đun subprocess, bao gồm cách chạy lệnh bên ngoài, chuyển hướng đầu vào và đầu ra, cũng như xử lý lỗi. Dù bạn là người mới bắt đầu hay lập trình viên Python giàu kinh nghiệm, bài hướng dẫn này sẽ cung cấp cho bạn kiến thức cần thiết để sử dụng hiệu quả mô-đun subprocess trong các dự án của mình.
Python Subprocess là gì?
Mô-đun subprocess của Python là một công cụ cho phép bạn chạy các chương trình hoặc lệnh khác từ mã Python của mình. Nó có thể được dùng để mở chương trình mới, gửi dữ liệu cho chúng và nhận lại kết quả.
Sử dụng mô-đun subprocess trong Python giống như bạn ra lệnh cho máy tính bằng Python thay vì gõ trực tiếp vào dòng lệnh. Mô-đun này giúp tự động hóa tác vụ và tích hợp chương trình khác với mã Python của bạn một cách dễ dàng. Chẳng hạn, bạn có thể dùng subprocess để chạy lệnh shell như ls hoặc ping và lấy đầu ra của lệnh đó trong mã Python. Bạn cũng có thể dùng nó để chạy script Python khác hoặc các tệp thực thi, như tệp .exe trên Windows.
Ngoài ra, mô-đun subprocess có thể chuyển hướng đầu vào và đầu ra của tiến trình, nghĩa là bạn có thể kiểm soát dữ liệu nào được gửi tới tiến trình và dữ liệu nào nhận về từ đó.
Một trong những khả năng hữu ích nhất của mô-đun subprocess là cho phép bạn xử lý đầu vào, đầu ra và lỗi (thường ở stderr) do tiến trình con tạo ra. Với check=True, một CalledProcessError sẽ được ném ra và bạn có thể kiểm tra e.stdout/e.stderr. Tính năng này được xem là một trong những điểm mạnh nhất của mô-đun. Nhờ vậy, việc gọi các tiến trình con trở nên mạnh mẽ và linh hoạt hơn. Ví dụ, giờ đây bạn có thể dùng đầu ra của subprocess như một biến trong suốt phần còn lại của script Python.
Khi nào nên dùng Python Subprocess
Hãy cùng xem khi nào nên dùng mô-đun subprocess của Python.
Tự động hóa các tác vụ hệ thống
Mô-đun subprocess có thể dùng để tự động hóa nhiều tác vụ hệ thống như chạy sao lưu, khởi động/dừng dịch vụ và lập lịch cron. Ví dụ, bạn có thể dùng subprocess để chạy lệnh cp tạo bản sao lưu tệp hoặc systemctl (Linux hiện đại) hoặc service (hệ thống cũ) để khởi động và dừng dịch vụ. Bạn cũng có thể dùng subprocess để gọi các công cụ lập lịch (như cron trên Linux hoặc Task Scheduler trên Windows), nhưng việc lập lịch thực tế do các công cụ đó xử lý, không phải do subprocess.
Chạy các công cụ dòng lệnh
Mô-đun subprocess có thể dùng để chạy các công cụ dòng lệnh như grep, sed và awk, rồi xử lý đầu ra trong mã Python của bạn. Ví dụ, bạn có thể dùng subprocess để chạy lệnh grep tìm mẫu cụ thể trong tệp và sau đó xử lý đầu ra trong Python. Điều này hữu ích cho các tác vụ như phân tích log, xử lý dữ liệu và thao tác văn bản.
Chạy các tệp thực thi bên ngoài
Mô-đun subprocess có thể chạy các tệp thực thi khác, như tệp .exe trên Windows, và kiểm soát hành vi của chúng. Ví dụ, bạn có thể dùng subprocess để chạy một tệp thực thi thực hiện tác vụ cụ thể rồi dùng đầu ra của nó trong mã của bạn. Điều này hữu ích cho xử lý ảnh, phân tích dữ liệu và các tác vụ máy học.
Chạy script như tiến trình nền
Bạn có thể dùng mô-đun subprocess để chạy script như tiến trình nền để chúng tiếp tục chạy sau khi chương trình chính thoát. Ví dụ, bạn có thể dùng subprocess để chạy một script thực hiện tác vụ cụ thể và thoát chương trình chính mà không chờ script hoàn tất. Điều này hữu ích cho giám sát, ghi log và thu thập dữ liệu.
Chạy script với trình thông dịch không phải Python
Mô-đun subprocess có thể giúp bạn chạy các script viết bằng ngôn ngữ khác như Perl, Ruby và Bash. Ví dụ, bạn có thể dùng subprocess để chạy một script Perl thực hiện tác vụ cụ thể rồi dùng đầu ra của script đó trong mã của bạn. Điều này hữu ích cho xử lý dữ liệu, thao tác văn bản và quản trị hệ thống.
Xử lý song song
Mô-đun subprocess có thể chạy nhiều tiến trình song song, hữu ích cho các tác vụ như xử lý ảnh, phân tích dữ liệu và máy học. Ví dụ, bạn có thể dùng subprocess để chạy nhiều phiên bản cùng một script, mỗi phiên bản xử lý một phần dữ liệu khác nhau rồi kết hợp kết quả.
Chạy và chỉnh sửa mã từ hướng dẫn trực tuyến này.
Chạy mãCác ví dụ về Python Subprocess
Giờ hãy cùng xem một số ví dụ về subprocess trong Python.
python subprocess run
Phương thức subprocess.run() là cách tiện lợi để chạy một subprocess và chờ nó hoàn tất. Bạn có thể chọn lệnh cần chạy và thêm các tùy chọn như đối số, biến môi trường và chuyển hướng vào/ra. Khi subprocess được khởi chạy, phương thức run() sẽ chặn cho tới khi tiến trình hoàn tất và trả về một đối tượng CompletedProcess, chứa mã trả về và đầu ra của subprocess.
Phương thức subprocess.run() nhận nhiều đối số, một số trong đó là:
-
args: Lệnh cần chạy và các đối số của nó, truyền vào dưới dạng danh sách chuỗi. -
capture_output: Khi đặt True, sẽ ghi lại standard output và standard error. -
text: Khi đặt True, sẽ trả về stdout và stderr dạng chuỗi, nếu không sẽ là bytes. -
check: giá trị boolean cho biết có kiểm tra mã trả về của subprocess hay không; nếu check là true và mã trả về khác 0, sẽ ném ra ngoại lệCalledProcessError. -
timeout: Giá trị tính bằng giây chỉ định thời gian chờ subprocess hoàn tất trước khi hết hạn. -
shell: Giá trị boolean cho biết có chạy lệnh trong shell hay không. Điều này có nghĩa là lệnh được truyền dưới dạng chuỗi và có thể dùng các tính năng đặc thù của shell, như mở rộng ký tự đại diện và thay thế biến.
Phương thức subprocess.run() cũng trả về một đối tượng CompletedProcess, với các thuộc tính sau:
-
args: Lệnh và đối số đã chạy. -
returncode: Mã trả về của subprocess. -
stdout: Standard output của subprocess (chuỗi nếu text=True, nếu không là bytes). stderr: Standard error của subprocess (chuỗi nếu text=True, nếu không là bytes).
Ví dụ 1: Chạy lệnh shell:
import subprocess
result = subprocess.run("dir", shell=True, capture_output=True, text=True)
print(result.stdout)
Đầu ra:
Volume in drive C has no label.
Volume Serial Number is E414-A41C
Directory of C:\Users\owner
01/25/2023 10:56 AM <DIR> .
01/25/2023 10:56 AM <DIR> ..
07/19/2021 01:19 PM <DIR> .anaconda
07/19/2021 01:19 PM <DIR> .astropy
07/19/2021 01:19 PM <DIR> .aws
09/12/2022 08:48 AM 496 .bash_history
03/27/2022 03:08 PM <DIR> .cache
09/26/2021 06:58 AM <DIR> .conda
09/26/2021 06:59 AM 25 .condarc
…
Trên Linux/macOS, dùng: result = subprocess.run(["ls", "-la"], capture_output=True, text=True) (không cần tham số shell).
Ví dụ 2: Chạy script Python:
Bạn cũng có thể chạy một script Python bằng phương thức subprocess.run(). Hãy bắt đầu bằng cách tạo một script Python đơn giản trong tệp .py
print(“This is the output from subprocess module”)
Lưu tệp này là file_donot_exist.py. Bây giờ, bạn có thể dùng mô-đun subprocess để chạy tệp này:
import subprocess
import sys
result = subprocess.run(["python", "file_donot_exist.py"], capture_output=True, text=True, check=False)
print(result.stdout)
Đầu ra:
This is the output from subprocess module
Ví dụ 3: Chạy mã Python trực tiếp từ một hàm:
Với các trường hợp đơn giản, bạn có thể truyền trực tiếp một lệnh Python vào hàm subprocess.run(). Cách làm như sau:
result = subprocess.run([sys.executable, "-c", "print('This is directly from a subprocess.run() function')"], capture_output = True, text = True)
print(result.stdout)
Đầu ra:
This is directly from a subprocess.run() function
Trong danh sách args, phần tử đầu tiên sys.executable được phân giải động thành đường dẫn của trình thông dịch Python hiện tại. Điều này đảm bảo nhất quán với tiến trình đang chạy và tránh hardcode đường dẫn như "C:/...". Phần tử thứ hai, "-c", chạy chuỗi theo sau như mã Python thay vì một script. Luôn import sys để truy cập sys.executable.
Ví dụ 4: Sử dụng đối số check
Đối số check là một tham số tùy chọn của hàm subprocess.run() trong mô-đun subprocess của Python. Đây là một giá trị boolean điều khiển việc hàm có kiểm tra mã trả về của lệnh đang chạy hay không.
Khi check được đặt True, hàm sẽ kiểm tra mã trả về của lệnh và ném ra ngoại lệ CalledProcessError nếu mã trả về khác 0. Ngoại lệ sẽ có các thuộc tính gồm mã trả về, stdout, stderr và lệnh.
Khi check được đặt False (mặc định), hàm sẽ không ném ngoại lệ ngay cả khi lệnh thất bại — thay vào đó, bạn cần tự kiểm tra mã trả về.
import subprocess
result = subprocess.run(["python", "file_donot_exist.py"], capture_output=True, text=True, check=True)
print(result.stdout)
print(result.stderr)
Đầu ra:
---------------------------------------------------------------------------
CalledProcessError Traceback (most recent call last)
<ipython-input-81-503b60184db8> in <module>
1 import subprocess
----> 2 result = subprocess.run(["python", "file_donot_exist.py"], capture_output=True, text=True, check=True)
3 print(result.stdout)
4 print(result.stderr)
~\anaconda3\lib\subprocess.py in run(input, capture_output, timeout, check, *popenargs, **kwargs)
514 retcode = process.poll()
515 if check and retcode:
--> 516 raise CalledProcessError(retcode, process.args,
517 output=stdout, stderr=stderr)
518 return CompletedProcess(process.args, retcode, stdout, stderr)
CalledProcessError: Command '['python', 'file_donot_exist.py']' returned non-zero exit status 2.
Lưu ý lệnh đã thất bại vì my_python_file2.py không tồn tại, và vì đã truyền check=True, một CalledProcessError đã được ném ra. Nếu bạn đặt check=False (hoặc bỏ qua), tiến trình của bạn sẽ không ném ngoại lệ; thay vào đó, bạn sẽ thấy thông báo lỗi trong stderr và có thể tự quyết định cách xử lý.
import subprocess
result = subprocess.run(["python", "my_python_file2.py"], capture_output=True, text=True, check=False)
print(result.stdout)
print(result.stderr)
Đầu ra:
python: can't open file 'my_python_file2.py': [Errno 2] No such file or directory
python subprocess Popen
subprocess.Popen() là giao diện cấp thấp hơn để chạy subprocess, trong khi subprocess.run là một lớp bao cấp cao hơn quanh Popen nhằm giúp sử dụng thuận tiện hơn.
Popen cho phép bạn khởi động một tiến trình mới và tương tác với các luồng standard input, output và error của nó. Nó trả về một handle tới tiến trình đang chạy để bạn có thể chờ tiến trình hoàn tất, kiểm tra mã trả về hoặc chấm dứt nó.
run() là hàm tiện lợi hơn, cho phép bạn chạy một lệnh và thu đầu ra chỉ trong một lần gọi, không cần tự tạo đối tượng Popen và quản lý các luồng. Nó cũng cho phép chỉ định nhiều tùy chọn khi chạy lệnh, như có ném ngoại lệ nếu lệnh thất bại hay không.
Nhìn chung, bạn nên dùng run() nếu chỉ cần chạy lệnh và lấy đầu ra, còn dùng Popen nếu cần kiểm soát nhiều hơn tiến trình, chẳng hạn tương tác với các luồng vào/ra của nó.
Lớp Popen nhận các đối số giống run(), bao gồm args chỉ định lệnh cần chạy và các tham số tùy chọn như stdin, stdout, stderr, shell, cwd và env. Ngoài ra, lớp Popen có nhiều phương thức cho phép bạn tương tác với tiến trình như communicate(), poll(), wait(), terminate() và kill().
import subprocess
p = subprocess.Popen(["python", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
output, errors = p.communicate()
print(output)
Đầu ra:
usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...
Options and arguments (and corresponding environment variables):
-b : issue warnings about str(bytes_instance), str(bytearray_instance)
and comparing bytes/bytearray with str. (-bb: issue errors)
-B : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x
-c cmd : program passed in as string (terminates option list)
-d : debug output from parser; also PYTHONDEBUG=x
-E : ignore PYTHON* environment variables (such as PYTHONPATH)
…
Đoạn này sẽ chạy lệnh python –help và tạo một đối tượng Popen mới, được lưu trong biến p. Standard output và error của lệnh được thu bằng phương thức communicate() và lần lượt lưu trong biến output và errors.
subprocess.Popen hữu ích khi bạn muốn kiểm soát nhiều hơn tiến trình, như gửi dữ liệu vào, nhận dữ liệu ra hoặc chờ nó hoàn tất.
python subprocess call
subprocess.call() là một hàm trong mô-đun subprocess của Python dùng để chạy một lệnh trong tiến trình riêng và chờ nó hoàn tất. Nó trả về mã trả về của lệnh, bằng 0 nếu lệnh thành công và khác 0 nếu thất bại.
Hàm call() nhận các đối số giống run(), bao gồm args chỉ định lệnh cần chạy và các tham số tùy chọn khác như stdin, stdout, stderr, shell, cwd và env.
Standard output và error của lệnh sẽ được gửi tới cùng stdout và stderr như tiến trình cha, trừ khi bạn chuyển hướng chúng bằng các tham số stdout và stderr.
import sys
import subprocess
return_code = subprocess.call([sys.executable, "--version"])
if return_code == 0:
print("Command executed successfully.")
else:
print("Command failed with return code", return_code)
Đầu ra:
Command executed successfully.
Đoạn này sẽ chạy lệnh python -–version trong một tiến trình riêng và chờ nó hoàn tất. Mã trả về của lệnh sẽ được lưu trong biến return_code, bằng 0 nếu lệnh thành công và khác 0 nếu thất bại.
subprocess.call() hữu ích khi bạn muốn chạy một lệnh và kiểm tra mã trả về, nhưng không cần thu đầu ra.
python subprocess check_output
check_output là một hàm trong mô-đun subprocess tương tự run(), nhưng nó chỉ trả về standard output của lệnh và ném ngoại lệ CalledProcessError nếu mã trả về khác 0.
Hàm check_output() nhận các đối số giống run(), bao gồm args (lệnh cần chạy) và các tham số tùy chọn như stdin, stderr, shell, cwd và env. Nó trả về standard output của lệnh dạng đối tượng bytes hoặc chuỗi (nếu text=True). Nó ném CalledProcessError nếu lệnh thất bại (trạng thái thoát khác 0).
Lưu ý: Theo mặc định, check_output() chỉ ghi lại stdout. Để đưa stderr vào đầu ra, hãy chuyển hướng rõ ràng bằng stderr=subprocess.STDOUT.
import subprocess
import sys
try:
output = subprocess.check_output([sys.executable, "--version"], text=True)
print(output)
except subprocess.CalledProcessError as e:
print(f"Command failed with return code {e.returncode}")
Đầu ra:
Python 3.8.8
Python Subprocess Pipe
Mô-đun subprocess của Python cung cấp cách tạo và tương tác với các tiến trình con, có thể dùng để chạy chương trình hoặc lệnh khác. Một trong các tính năng của subprocess là khả năng tạo pipe, cho phép giao tiếp giữa tiến trình cha và tiến trình con.
Pipe là kênh liên lạc một chiều, kết nối standard output của một tiến trình với standard input của tiến trình khác. Pipe có thể nối đầu ra của một lệnh với đầu vào của lệnh khác, cho phép dùng đầu ra của lệnh thứ nhất làm đầu vào cho lệnh thứ hai.
Pipe có thể được tạo bằng mô-đun subprocess với lớp Popen bằng cách chỉ định tham số stdout hoặc stdin là subprocess.PIPE.
Ví dụ, đoạn mã sau tạo một pipe kết nối đầu ra của lệnh ls với đầu vào của lệnh grep, lệnh này lọc để chỉ hiển thị các dòng chứa từ "file":
import subprocess
ls_process = subprocess.Popen(["ls"], stdout=subprocess.PIPE, text=True)
grep_process = subprocess.Popen(
["grep", "sample"],
stdin=ls_process.stdout,
stdout=subprocess.PIPE,
text=True
)
ls_process.stdout.close() # POSIX-only: prevents deadlocks by closing the pipe
output, error = grep_process.communicate()
print(output)
print(error)
Đầu ra:
sample_data
Trong ví dụ này, lớp Popen được dùng để tạo hai tiến trình con, một cho lệnh ls và một cho lệnh grep. Stdout của lệnh ls được nối với stdin của lệnh grep bằng subprocess.PIPE, tạo ra một pipe giữa hai tiến trình. Phương thức communicate() được dùng để gửi đầu ra của lệnh ls tới lệnh grep và lấy về đầu ra đã lọc.
Kết luận
Mô-đun subprocess của Python cung cấp cách mạnh mẽ và linh hoạt để tạo và tương tác với tiến trình con, cho phép bạn chạy chương trình khác hoặc phát lệnh ngay trong script Python. Từ các lệnh đơn giản như subprocess.call() đến các tính năng nâng cao như pipe, chuyển hướng đầu vào/đầu ra và truyền biến môi trường, mô-đun subprocess có giải pháp cho hầu hết mọi trường hợp sử dụng. Đây là cách tuyệt vời để tự động hóa tác vụ lặp lại, chạy lệnh hệ thống và thậm chí tương tác với ngôn ngữ lập trình hay nền tảng khác.
Khi làm việc với mô-đun subprocess, cần nhớ rằng chạy lệnh bên ngoài tiềm ẩn rủi ro bảo mật, đặc biệt khi dùng tham số shell=True hoặc truyền đầu vào chưa được lọc. Luôn là thực hành tốt khi dùng hàm subprocess.run() cho phép bạn chỉ định nhiều tùy chọn về cách chạy lệnh, như có ném ngoại lệ nếu lệnh thất bại hay không.
Nếu bạn muốn đào sâu vào vô vàn khả năng tự động hóa dòng lệnh bằng Python, hãy xem khóa học Command Line Automation trong Python của chúng tôi. Trong khóa học này, bạn sẽ học cách viết mã tự động hóa để duyệt hệ thống tệp, tìm các tệp theo mẫu, và xác định xem tệp có trùng lặp trong nhiều trường hợp hay không. Sau khi hoàn thành khóa học, bạn sẽ có thể quản lý và tương tác với các tiến trình Unix cũng như tự động hóa nhiều hoạt động thường nhật trên hệ thống tệp.
Nâng tầm kỹ năng Python cho đội ngũ của bạn với DataCamp for Business
Nếu bạn hoặc đội ngũ của bạn đang muốn nâng cao kỹ năng về Python và tự động hóa dòng lệnh, hãy cân nhắc khám phá DataCamp for Business. DataCamp cung cấp giải pháp học tập theo nhu cầu cho các đội ngũ ở mọi quy mô, giúp doanh nghiệp luôn dẫn đầu trong bối cảnh công nghệ thay đổi nhanh chóng. Với DataCamp for Business, bạn có thể nâng cấp kỹ năng cho đội ngũ bằng các khóa học và lộ trình học tùy chỉnh được thiết kế để xây dựng chuyên môn về Python, tự động hóa và các công cụ khoa học dữ liệu thiết yếu khác. Cho dù bạn là startup hay doanh nghiệp lớn, DataCamp for Business mang đến nguồn lực và tính linh hoạt để đạt được mục tiêu học tập của đội ngũ. Yêu cầu bản demo ngay hôm nay để tìm hiểu thêm.
Câu hỏi thường gặp về Python Subprocess
Sự khác biệt giữa subprocess.call() và subprocess.run() là gì?
subprocess.call() là hàm chạy một lệnh và chờ hoàn tất, trả về mã trả về của lệnh. subprocess.run() là hàm mạnh mẽ hơn cho phép bạn chạy lệnh, thu đầu ra và chỉ định nhiều tùy chọn về cách chạy lệnh, như có ném ngoại lệ nếu lệnh thất bại hay không.
Làm thế nào để chạy lệnh và thu đầu ra trong Python bằng subprocess?
Bạn có thể dùng hàm subprocess.run() để chạy lệnh và thu đầu ra chỉ trong một lần gọi. Ví dụ, đoạn mã sau chạy lệnh ls và ghi lại đầu ra vào biến result:
import subprocess
result = subprocess.run(["ls"], stdout=subprocess.PIPE)
print(result.stdout.decode())
Làm sao truyền biến làm đối số cho lệnh trong subprocess?
Bạn có thể truyền biến làm đối số cho lệnh trong subprocess bằng cách đưa chúng vào danh sách truyền cho hàm subprocess.run() hoặc subprocess.Popen(). Ví dụ, đoạn mã sau chạy lệnh echo và truyền giá trị của biến message làm đối số:
import subprocess
message = "Hello, World!"
subprocess.run(["echo", message])
Làm thế nào để chuyển hướng đầu ra của lệnh vào tệp bằng subprocess?
Bạn có thể chuyển hướng đầu ra của lệnh vào tệp bằng toán tử > trong lệnh. Ví dụ, đoạn mã sau chạy lệnh ls và chuyển hướng đầu ra của nó vào tệp tên ls.txt:
import subprocess
subprocess.run("ls > ls.txt", shell=True)Làm sao chạy lệnh ở chế độ nền bằng subprocess?
Bạn có thể chạy một lệnh ở chế độ nền bằng cách đặt tham số start_new_session thành True khi tạo tiến trình mới bằng subprocess.Popen(). Điều này sẽ tạo một nhóm tiến trình mới, cho phép bạn chạy lệnh ở chế độ nền.
import subprocess
subprocess.Popen(["ls"], start_new_session=True)
Làm sao kiểm tra mã trả về của lệnh chạy bằng subprocess?
Bạn có thể kiểm tra mã trả về của lệnh bằng cách truy cập thuộc tính returncode của đối tượng subprocess.CompletedProcess do subprocess.run() trả về. Ví dụ, đoạn mã sau chạy lệnh ls và kiểm tra mã trả về:
import subprocess
result = subprocess.run(["ls"])
if result.returncode == 0:
print("Command ran successfully")
else:
print("Command failed with error code", result.returncode)
Làm sao truyền biến môi trường cho lệnh chạy bằng subprocess?
Bạn có thể truyền biến môi trường cho một lệnh bằng cách đưa chúng vào một từ điển và truyền từ điển đó qua tham số env khi tạo tiến trình mới bằng subprocess.Popen() hoặc subprocess.run(). Ví dụ, đoạn mã sau đặt biến môi trường TEST_VAR và chạy lệnh printenv:
import subprocess
env = {'TEST_VAR': 'test_value'}
subprocess.run(["printenv", "TEST_VAR"], env=env)
