Python đợi dòng trước kết thúc

Là một ngôn ngữ kịch bản, Python là một lựa chọn tốt cho tự động hóa. Bạn có thể viết mã Python đơn giản để xâu chuỗi và tự động hóa việc thực thi các chương trình khác nhau. Và khi công việc kết thúc, bạn có thể viết một số tập lệnh Python để trích xuất và phân tích kết quả. Một số chương trình, chẳng hạn như Scrapy, hiện có API mà Python có thể gọi trực tiếp, điều đó có nghĩa là bạn có thể chạy trực tiếp các chương trình này bằng mã Python gốc. Tuy nhiên, nhiều người vẫn chưa có API thích hợp cho Python và bạn vẫn cần chạy trực tiếp các lệnh shell

Hình ảnh từ Pixabay

Tôi nghĩ rằng hầu hết các nhà phát triển Python đã sử dụng os.system() để thực thi các lệnh shell trước đây. Có, hàm system() của mô-đun os là cách đơn giản nhất để thực thi lệnh trình bao trong Python. Tuy nhiên, nó có hai nhược điểm lớn

Thứ nhất, bạn chỉ có thể lấy mã thoát của lệnh shell và không thể lấy đầu ra tiêu chuẩn hoặc lỗi từ os.system(). Chà, đây có thể không phải là vấn đề nếu bạn không quan tâm đến đầu ra hoặc thông báo lỗi của lệnh shell và chỉ muốn chạy nó. Trong trường hợp này, hãy chú ý thêm một chút đến nhược điểm thứ hai, điều này quan trọng hơn

Thứ hai, os.system() dễ bị tiêm vỏ, còn được gọi là tiêm lệnh hệ điều hành. Đây là sự cố bảo mật nghiêm trọng nếu chương trình của bạn chấp nhận đầu vào của người dùng sẽ được sử dụng trong hàm os.system(). Bây giờ chúng ta sẽ chứng minh vấn đề này bằng một ví dụ đơn giản. Giả sử chúng ta có một số tập lệnh chấp nhận đầu vào của người dùng cho tên máy chủ hoặc địa chỉ IP sẽ được sử dụng làm đầu vào cho lệnh ping để kiểm tra kết nối mạng. Đầu vào của người dùng có thể đến từ giao diện người dùng hoặc đơn giản là với chức năng input

Và bạn có thể thấy kết quả của lệnh ping

Giả sử một người dùng độc ác nhập một số lệnh như thế này

Và bạn sẽ thấy một điều thú vị, nhưng cũng khá nguy hiểm

Với hàm os.system(), chuỗi được truyền vào được phân tích cú pháp dưới dạng các lệnh trình bao Linux. Đặc biệt dấu chấm phẩy system()0 dùng để ngăn cách giữa các lệnh. Do đó, trong ví dụ trên, nếu người dùng nhập vào system()1, hai lệnh sẽ được thực thi. Cái đầu tiên là system()2, và cái thứ hai là system()3

Đây là một vấn đề bảo mật nghiêm trọng nếu nội dung của system()4 bị lộ cho người dùng bên ngoài. Ngoài ra, người dùng có thể nhập bất kỳ lệnh shell Linux nào sau dấu chấm phẩy. Giả sử người dùng nhập một lệnh phá hoại như system()5, thì lệnh này có thể gây ra thiệt hại nghiêm trọng cho hệ thống của bạn

Do đó, để thực hành tốt nhất, hãy tránh sử dụng os.system() mà thay vào đó hãy sử dụng mô-đun quy trình con, mô-đun này sẽ sớm được giới thiệu. Nếu không thể tránh được, không chấp nhận đầu vào của người dùng hoặc làm sạch đầu vào của người dùng trước khi nó được chuyển đến os.system() để không có lệnh nguy hiểm nào có thể được thực thisystem()8

Sử dụng system()9

Tôi nghĩ bạn đã nhận ra sự nguy hiểm của hàm os.system(). Thay vào đó, nên sử dụng mô-đun quy trình con để thực thi các lệnh trình bao bất cứ khi nào có thể. Nó có thể thực hiện công việc tương tự như os.system() và hơn thế nữa. Và quan trọng hơn, chúng ta có thể tránh được việc tiêm vỏ giúp sử dụng an toàn hơn

Khi bạn đọc một số hướng dẫn hoặc bài đăng cũ, bạn sẽ thấy nhiều chức năng từ mô-đun quy trình con được sử dụng, bao gồm os2, os3, os4, v.v. Tuy nhiên, với các phiên bản Python hiện đại (>3. 5), bạn nên sử dụng hàm os3 cho tất cả các trường hợp sử dụng

Cách tiếp cận được đề xuất để gọi các quy trình con là sử dụng hàm os3 cho tất cả các trường hợp sử dụng mà nó có thể xử lý. Đối với các trường hợp sử dụng nâng cao hơn, giao diện os7 cơ bản có thể được sử dụng trực tiếp

Hầu hết thời gian, chúng ta chỉ cần sử dụng hàm os3, sử dụng lớp os7 dưới mui xe. Đối với các trường hợp đặc biệt khi bạn muốn chạy các lệnh shell không đồng bộ, bạn có thể sử dụng trực tiếp lớp os7, không giống như os3, sẽ không đợi lệnh shell kết thúc

Chúng tôi sẽ giới thiệu lớp os.system()2 sau. Bây giờ, hãy xem cách sử dụng hàm os3 để tránh tiêm shell

Hàm os.system()4 có cờ os.system()5. Nếu cờ này là os.system()6, thì nó hoạt động tương tự như hàm os.system() và chấp nhận một chuỗi làm đầu vào. Do đó, nó có cùng vấn đề bảo mật như os.system()8

Chúng ta có thể thấy nó có cùng một kết quả và do đó, cùng một vấn đề như os.system() như hình trên

Mặt khác, nếu cờ os.system()5 là os.system()1, điều đó có nghĩa là lệnh trình bao sẽ được chuyển vào dưới dạng danh sách thay vì chuỗi. Hãy xem nếu tiêm vỏ vẫn xảy ra trong trường hợp này

Lưu ý rằng số 3 cũng phải được chuyển vào dưới dạng chuỗi, nếu không sẽ xảy ra ngoại lệ. Thông báo lỗi sẽ là
os.system()2

Thật thú vị, lần này chức năng os.system()3 không thành công. Nếu bạn kiểm tra cẩn thận, bạn có thể thấy rằng system()1 được truyền toàn bộ và không chia thành hai lệnh trình bao. Điều đó có nghĩa là toàn bộ system()1 được chuyển làm đầu vào cho lệnh ping, vì system()1 không phải là tên máy chủ hoặc địa chỉ IP hợp lệ, lệnh ping sẽ không thành công, như đã chỉ ra trong nhận xét

Do đó, với cờ os.system()5 được đặt thành os.system()1, chúng tôi có thể chấp nhận đầu vào của người dùng một cách an toàn và tránh tiêm shell cho hàm os.system()3. Vì os.system()1 là giá trị mặc định cho cờ os.system()5, nên chúng tôi không cần đặt rõ ràng nó là os.system()1. Tuy nhiên, bạn nên tránh đặt thành os.system()6 trừ khi bạn có lý do chính đáng để làm như vậy

Nhận đầu ra tiêu chuẩn và lỗi

Bên cạnh việc khắc phục sự cố chèn shell, hàm os.system()3 còn có các thuộc tính hữu ích khác. Hãy khám phá chúng một chút

Không giống như os.system() in đầu ra nổi bật (STDOUT) và lỗi tiêu chuẩn (STDERR) trên màn hình, os.system()3 có thể ghi lại đầu ra và lỗi và cung cấp chúng trong mã

Điều quan trọng là, với các tùy chọn os.system()9 và ping0, tiêu chuẩn ra và lỗi được lưu vào các thuộc tính ping1 và ping2 của kết quả, thuộc loại ping3. Bên cạnh đó, ping4 chỉ định rằng thiết bị xuất chuẩn và thiết bị xuất chuẩn phải có dữ liệu chuỗi thô, thay vì byte

Sử dụng ping5

Như chúng ta đã nhận thấy, hàm os.system()3 chặn cho đến khi lệnh shell hoàn thành. Điều này có thể không thích hợp hơn nếu lệnh shell mất nhiều thời gian để hoàn thành. Bạn có thể muốn chạy lệnh shell ở chế độ nền và làm việc khác trong thời gian chờ đợi. Với mục đích này, bạn có thể sử dụng trực tiếp lớp ping7, được sử dụng bởi hàm os.system()3 dưới mui xe

Các điểm chính cho đoạn mã trên

  • ping9 có các tùy chọn giống như os.system()3
  • ping9 không bị chặn và trả về ngay lập tức. Lệnh shell được chạy trong nền
  • Kết quả trả về của ping9 là kiểu input3. Chúng ta có thể sử dụng phương thức input4 để kiểm tra xem lệnh shell đã hoàn thành chưa. Nếu lệnh shell chưa được hoàn thành, thì ___7____5 được trả về. Nếu không, mã thoát của lệnh shell được trả về. Đặc biệt, mã thoát 0 có nghĩa là lệnh shell đã hoàn thành thành công
  • Đầu ra tiêu chuẩn và lỗi của lệnh trình bao được trả về bằng phương thức input6, chứ không phải bởi các thuộc tính ping1 và ping2 như với os.system()3

Có nhiều cài đặt hơn cho ping9. Ví dụ: bạn có thể đợi một tiến trình con đang chạy lệnh shell kết thúc bằng phương thức ping1 hoặc bạn có thể chuyển thời gian chờ cho phương thức ping2. Bạn sẽ khá dễ dàng để giải quyết những trường hợp này khi bạn biết những kiến ​​thức cơ bản về ping9

Trong bài đăng này, chúng tôi đã giới thiệu các cách khác nhau để thực thi lệnh shell trong Python. Theo nguyên tắc chung, chúng ta nên tránh sử dụng os.system() mà thay vào đó hãy sử dụng os.system()3 (với os.system()5 được đặt thành os.system()1, theo mặc định). Và nếu bạn cần chạy các lệnh shell không đồng bộ trong nền, bạn có thể sử dụng ping9 để thay thế. Với cả os.system()3 và ping9, đầu ra tiêu chuẩn và lỗi có thể được lấy và sử dụng trực tiếp trong mã Python của bạn

Python có đợi một quy trình con kết thúc không?

Hầu hết tương tác của bạn với mô-đun quy trình con Python sẽ thông qua hàm run(). Chức năng chặn này sẽ bắt đầu một quy trình và đợi cho đến khi quy trình mới thoát ra trước khi tiếp tục . Tài liệu khuyến nghị sử dụng run() cho tất cả các trường hợp mà nó có thể xử lý.

Cuộc gọi quy trình con có chờ hoàn thành không?

Mô-đun quy trình con cung cấp một chức năng có tên gọi. Hàm này cho phép bạn gọi một chương trình khác, đợi lệnh hoàn thành rồi trả về mã trả về.