Thứ năm, 30/07/2020 | 00:00 GMT+7

Cách sử dụng quy trình con để chạy các chương trình bên ngoài trong Python 3

Python 3 bao gồm mô-đun quy subprocess để chạy các chương trình bên ngoài và đọc kết quả kết quả của chúng bằng mã Python của bạn.

Bạn có thể thấy quy subprocess hữu ích nếu bạn muốn sử dụng một chương trình khác trên máy tính của bạn từ trong mã Python của bạn. Ví dụ: bạn có thể cần gọi git từ trong mã Python của bạn để truy xuất các file trong dự án của bạn được theo dõi trong kiểm soát version git . Vì bất kỳ chương trình nào bạn có thể truy cập trên máy tính của bạn đều có thể được điều khiển bởi quy subprocess , các ví dụ được hiển thị ở đây sẽ có thể áp dụng cho bất kỳ chương trình bên ngoài nào bạn có thể cần gọi từ mã Python của bạn .

subprocess bao gồm một số lớp học và chức năng, nhưng trong hướng dẫn này ta sẽ giới thiệu một trong những subprocess của các chức năng hữu ích nhất: subprocess.run . Ta sẽ xem xét các cách sử dụng khác nhau và các đối số từ khóa chính của nó.

Yêu cầu

Để tận dụng tối đa hướng dẫn này, bạn nên làm quen với lập trình bằng Python 3. Bạn có thể xem lại các hướng dẫn này để biết thông tin cơ bản cần thiết:

Chạy một chương trình bên ngoài

Bạn có thể sử dụng hàm subprocess.run để chạy chương trình bên ngoài từ mã Python của bạn . Tuy nhiên, trước tiên, bạn cần nhập quy subprocess và module sys vào chương trình của bạn :

import subprocess import sys  result = subprocess.run([sys.executable, "-c", "print('ocean')"]) 

Nếu bạn chạy điều này, bạn sẽ nhận được kết quả như sau:

Output
ocean

Hãy xem lại ví dụ này:

  • sys.executable là đường dẫn tuyệt đối đến file thực thi Python mà chương trình của bạn ban đầu được gọi. Ví dụ: sys.executable có thể là một đường dẫn như /usr/local/bin/python .
  • subprocess.run được cung cấp một danh sách các chuỗi bao gồm các thành phần của lệnh mà ta đang cố gắng chạy. Vì chuỗi đầu tiên ta truyền là sys.executable , ta đang hướng dẫn subprocess.run thực thi một chương trình Python mới.
  • Thành phần -c là một tùy chọn dòng lệnh python cho phép bạn truyền một chuỗi với toàn bộ chương trình Python để thực thi. Trong trường hợp của ta , ta truyền một chương trình in ra ocean chuỗi.

Bạn có thể coi mỗi mục nhập trong danh sách mà ta chuyển đến subprocess.run như được phân tách bằng dấu cách. Ví dụ: [sys.executable, "-c", "print('ocean')"] dịch gần đúng thành /usr/local/bin/python -c "print('ocean')" . Lưu ý quy subprocess tự động trích dẫn các thành phần của lệnh trước khi cố gắng chạy chúng trên hệ điều hành cơ bản để ví dụ: bạn có thể chuyển một tên file có khoảng trắng trong đó.

Cảnh báo: Không bao giờ chuyển đầu vào không tin cậy cho subprocess.run . Vì subprocess.run có khả năng thực hiện các lệnh tùy ý trên máy tính của bạn, các tác nhân độc hại có thể sử dụng nó để thao túng máy tính của bạn theo những cách không mong muốn.

Bắt kết quả từ một chương trình bên ngoài

Bây giờ ta có thể gọi một chương trình bên ngoài bằng cách sử dụng subprocess.run , hãy xem cách ta có thể nắm bắt kết quả từ chương trình đó. Ví dụ: quá trình này có thể hữu ích nếu ta muốn sử dụng git ls-files để xuất tất cả các file của bạn hiện được lưu trữ dưới sự kiểm soát của version .

Lưu ý: Các ví dụ hiển thị trong phần này yêu cầu Python 3.7 hoặc cao hơn. Đặc biệt, các đối số từ khóa capture_outputtext đã được thêm vào trong Python 3.7 khi nó được phát hành vào tháng 6 năm 2018.

Hãy thêm vào ví dụ trước của ta :

import subprocess import sys  result = subprocess.run(     [sys.executable, "-c", "print('ocean')"], capture_output=True, text=True ) print("stdout:", result.stdout) print("stderr:", result.stderr) 

Nếu ta chạy mã này, ta sẽ nhận được kết quả như sau:

Output
stdout: ocean stderr:

Ví dụ này phần lớn giống với ví dụ được giới thiệu trong phần đầu tiên: ta vẫn đang chạy một quy trình con để in ocean . Tuy nhiên, điều quan trọng là ta chuyển các đối số từ khóa capture_output=Truetext=True vào subprocess.run .

subprocess.run trả về một đối tượng subprocess.CompletedProcess được ràng buộc với result . Đối tượng subprocess.CompletedProcess bao gồm các chi tiết về mã thoát của chương trình bên ngoài và kết quả của nó. capture_output=True đảm bảo result.stdoutresult.stderr được điền bằng kết quả tương ứng từ chương trình bên ngoài. Theo mặc định, result.stdoutresult.stderr được ràng buộc dưới dạng byte, nhưng đối số từ khóa text=True hướng dẫn Python thay vào đó giải mã các byte thành chuỗi.

Trong phần kết quả , stdoutocean (cộng với dòng mới ở cuối print thêm ẩn) và ta không có stderr .

Hãy thử một ví dụ tạo ra một giá trị không trống cho stderr :

import subprocess import sys  result = subprocess.run(     [sys.executable, "-c", "raise ValueError('oops')"], capture_output=True, text=True ) print("stdout:", result.stdout) print("stderr:", result.stderr) 

Nếu ta chạy mã này, ta nhận được kết quả như sau:

Output
stdout: stderr: Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops

Mã này chạy một quy trình con Python ngay lập tức tạo ra một ValueError . Khi ta kiểm tra kết result cuối cùng, ta không thấy gì trong stdout và một Traceback của ValueError trong stderr . Điều này là do theo mặc định, Python viết Traceback của ngoại lệ chưa được xử lý vào stderr .

Nâng cao một ngoại lệ trên một mã thoát xấu

Đôi khi, rất hữu ích khi nêu ra một ngoại lệ nếu một chương trình ta chạy thoát với mã thoát không hợp lệ. Các chương trình thoát với mã 0 được coi là thành công, nhưng các chương trình thoát với mã khác 0 được coi là đã gặp lỗi. Ví dụ: mẫu này có thể hữu ích nếu ta muốn đưa ra một ngoại lệ trong trường hợp ta chạy git ls-files trong một folder thực sự không phải là repository git .

Ta có thể sử dụng đối số từ khóa check=True cho subprocess.run để có một ngoại lệ được đưa ra nếu chương trình bên ngoài trả về mã thoát khác 0:

import subprocess import sys  result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True) 

Nếu ta chạy mã này, ta nhận được kết quả như sau:

Output
Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 512, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

Kết quả này cho thấy rằng ta đã chạy một quy trình con gây ra lỗi, quy trình này được in bằng stderr trong terminal của ta . Sau đó, subprocess.run đã đưa ra một quy subprocess.CalledProcessError thay mặt ta trong chương trình Python chính của ta .

Ngoài ra, module quy subprocess cũng bao gồm phương thức subprocess.CompletedProcess.check_returncode , mà ta có thể gọi để có hiệu ứng tương tự:

import subprocess import sys  result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"]) result.check_returncode() 

Nếu ta chạy mã này, ta sẽ nhận được:

Output
Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 444, in check_returncode raise CalledProcessError(self.returncode, self.args, self.stdout, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

Vì ta không chuyển check=True cho subprocess.run , ta đã liên kết thành công một cá thể subprocess.CompletedProcess để result đến result mặc dù chương trình của ta đã thoát với mã khác 0. Tuy nhiên, việc gọi result.check_returncode() sẽ tạo ra một quy subprocess.CalledProcessError vì nó phát hiện quá trình đã hoàn thành được thoát ra bằng một mã không hợp lệ.

Sử dụng thời gian chờ để thoát khỏi chương trình sớm

subprocess.run bao gồm đối số timeout để cho phép bạn dừng một chương trình bên ngoài nếu mất quá nhiều thời gian để thực thi:

import subprocess import sys  result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1) 

Nếu ta chạy mã này, ta sẽ nhận được kết quả như sau:

Output
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 491, in run stdout, stderr = process.communicate(input, timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1024, in communicate stdout, stderr = self._communicate(input, endtime, timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1892, in _communicate self.wait(timeout=self._remaining_time(endtime)) File "/usr/local/lib/python3.8/subprocess.py", line 1079, in wait return self._wait(timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1796, in _wait raise TimeoutExpired(self.args, timeout) subprocess.TimeoutExpired: Command '['/usr/local/bin/python', '-c', 'import time; time.sleep(2)']' timed out after 0.9997982999999522 seconds

Quá trình con mà ta cố gắng chạy đã sử dụng chức năng time.sleep để ngủ trong 2 giây. Tuy nhiên, ta đã chuyển đối số từ khóa timeout=1 cho subprocess.run để hết thời gian xử lý con sau 1 giây. Điều này giải thích lý do tại sao lệnh gọi subprocess.run của ta cuối cùng lại tạo ra một ngoại lệ subprocess.TimeoutExpired .

Lưu ý đối số từ khóa timeout cho subprocess.run là gần đúng. Python sẽ cố gắng hết sức để giết quá trình con sau số giây timeout , nhưng nó không nhất thiết phải chính xác.

Chuyển đầu vào cho các chương trình

Đôi khi các chương trình mong đợi đầu vào được chuyển cho chúng qua stdin .

Đối số từ khóa input cho subprocess.run cho phép bạn truyền dữ liệu vào stdin của quy trình con. Ví dụ:

import subprocess import sys  result = subprocess.run(     [sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater" ) 

Ta sẽ nhận được kết quả như sau sau khi chạy mã này:

Output
underwater

Trong trường hợp này, ta chuyển các byte underwater input . Quy trình con mục tiêu của ta đã sử dụng sys.stdin để đọc nội dung được truyền trong stdin ( underwater ) và in nó ra trong kết quả của ta .

Đối số từ khóa input có thể hữu ích nếu bạn muốn xâu chuỗi nhiều lệnh gọi subprocess.run lại với nhau để chuyển kết quả của một chương trình làm đầu vào cho chương trình khác.

Kết luận

Mô-đun quy subprocess là một phần mạnh mẽ của thư viện chuẩn Python cho phép bạn chạy các chương trình bên ngoài và kiểm tra kết quả của chúng một cách dễ dàng. Trong hướng dẫn này, bạn đã học cách sử dụng subprocess.run để điều khiển các chương trình bên ngoài, chuyển đầu vào cho chúng, phân tích cú pháp kết quả của chúng và kiểm tra mã trả về của chúng.

Mô-đun quy subprocess hiển thị các lớp và tiện ích bổ sung mà ta chưa đề cập trong hướng dẫn này. Đến đây bạn đã có đường cơ sở, bạn có thể sử dụng tài liệu của module quy subprocess để tìm hiểu thêm về các lớp và tiện ích có sẵn khác.


Tags:

Các tin liên quan

Làm thế nào để đánh lừa một mạng neural trong Python 3
2020-07-30
Cách sử dụng hàm bộ lọc Python
2020-07-24
Cách sử dụng module pathlib để thao tác đường dẫn hệ thống tệp trong Python 3
2020-07-15
Cách tạo Slackbot bằng Python trên Ubuntu 20.04
2020-06-30
Cách sử dụng ThreadPoolExecutor trong Python 3
2020-06-23
Cách sử dụng module sqlite3 trong Python 3
2020-06-02
Cách thiết lập notebook Jupyter với Python 3 trên Ubuntu 20.04 và Kết nối qua Đường hầm SSH
2020-05-19
Cách cài đặt Phân phối Python Anaconda trên Ubuntu 20.04 [Khởi động nhanh]
2020-05-19
Cách cài đặt Phân phối Python Anaconda trên Ubuntu 20.04
2020-05-06
Cách cài đặt Python 3 và thiết lập môi trường lập trình trên server Ubuntu 20.04
2020-04-24