Tại sao sử dụng các phương thức mô-đun os của Python thay vì thực hiện trực tiếp các lệnh shell?


157

Tôi đang cố gắng hiểu động lực đằng sau việc sử dụng các hàm thư viện của Python để thực thi các tác vụ dành riêng cho hệ điều hành như tạo tệp / thư mục, thay đổi thuộc tính tệp, v.v. thay vì chỉ thực hiện các lệnh đó thông qua os.system()hoặc subprocess.call()?

Ví dụ, tại sao tôi muốn sử dụng os.chmodthay vì làm os.system("chmod...")?

Tôi hiểu rằng việc sử dụng các phương thức thư viện có sẵn của Python càng nhiều càng tốt thay vì chỉ thực hiện trực tiếp các lệnh shell. Nhưng, có động lực nào khác đằng sau việc làm này từ quan điểm chức năng không?

Tôi chỉ nói về việc thực hiện các lệnh shell một dòng đơn giản ở đây. Khi chúng ta cần kiểm soát nhiều hơn đối với việc thực thi tác vụ, tôi hiểu rằng việc sử dụng subprocessmô-đun có ý nghĩa hơn chẳng hạn.


6
Bạn cơ bản đánh vào đầu đinh. Các tác vụ cấp hệ điều hành mà bạn đề cập đến đủ phổ biến để chúng đảm bảo chức năng của chính chúng thay vì chỉ bị xuống hạng để được gọi thông qua os.system.
deweyredman 17/2/2015

7
BTW, bạn đã cố gắng để thời gian thực hiện thời gian - os.chmod so với os.system ("chmod ...") . Tôi sẽ mạo hiểm đoán rằng nó sẽ trả lời một phần câu hỏi của bạn.
núi lửa

61
Tại sao có printkhi bạn có thể os.system("echo Hello world!")?
dùng253751

25
Vì lý do tương tự, bạn nên sử dụng os.pathđể xử lý các đường dẫn thay vì xử lý chúng theo cách thủ công: nó hoạt động trên mọi HĐH nơi nó chạy.
Bakuriu

51
"Thực hiện các lệnh shell trực tiếp" thực sự ít trực tiếp hơn . Shell không phải là giao diện cấp thấp cho hệ thống và os.chmodsẽ không gọi chmodchương trình mà shell sẽ. Sử dụng os.system('chmod ...')ra mắt một cái vỏ để giải thích một chuỗi gọi khác thực thi để thực hiện một cuộc gọi đến C chmodchức năng, trong khi os.chmod(...)đi nhiều hơn nữa trực tiếp vào thư mục C chmod.
user2357112 hỗ trợ Monica

Câu trả lời:


325
  1. nhanh hơn , os.systemsubprocess.calltạo ra quy trình mới đó là không cần thiết cho một cái gì đó đơn giản này. Trong thực tế, os.systemsubprocess.callvới shellđối số thường tạo ra ít nhất hai quy trình mới: quy trình đầu tiên là hệ vỏ và quy trình thứ hai là lệnh mà bạn đang chạy (nếu đó không phải là một vỏ được tích hợp sẵn test).

  2. Một số lệnh là vô dụng trong một quy trình riêng biệt . Ví dụ, nếu bạn chạy os.spawn("cd dir/"), nó sẽ thay đổi thư mục làm việc hiện tại của quy trình con, nhưng không thay đổi quy trình Python. Bạn cần sử dụng os.chdircho điều đó.

  3. Bạn không phải lo lắng về các ký tự đặc biệt được giải thích bởi shell. os.chmod(path, mode)sẽ hoạt động bất kể tên tệp là gì, trong khi đó os.spawn("chmod 777 " + path)sẽ thất bại khủng khiếp nếu tên tệp là như thế ; rm -rf ~. (Lưu ý rằng bạn có thể giải quyết vấn đề này nếu bạn sử dụng subprocess.callmà không cần shellđối số.)

  4. Bạn không phải lo lắng về tên tệp bắt đầu bằng dấu gạch ngang . os.chmod("--quiet", mode)sẽ thay đổi quyền của tệp có tên --quiet, nhưng os.spawn("chmod 777 --quiet")sẽ thất bại, như --quietđược hiểu là một đối số. Điều này đúng ngay cả đối với subprocess.call(["chmod", "777", "--quiet"]).

  5. Bạn có ít mối quan tâm đa nền tảngđa nền tảng hơn , vì thư viện tiêu chuẩn của Python có nghĩa vụ phải giải quyết vấn đề đó cho bạn. Hệ thống của bạn có chmodlệnh không? Nó đã được cài đặt chưa? Nó có hỗ trợ các tham số mà bạn mong đợi nó hỗ trợ không? Các osmô-đun sẽ cố gắng để được như cross-platform càng tốt và các văn bản khi nó là không thể.

  6. Nếu lệnh bạn đang chạy có đầu ra mà bạn quan tâm, bạn cần phân tích cú pháp, nó khó hơn âm thanh, vì bạn có thể quên các trường hợp góc (tên tệp có dấu cách, tab và dòng mới trong chúng), ngay cả khi bạn không quan tâm đến tính di động.


38
Để thêm vào điểm "đa nền tảng", liệt kê một thư mục là "ls" trên linux, "dir" trên windows. Lấy nội dung của một thư mục là một nhiệm vụ cấp thấp rất phổ biến.
Cort Ammon

1
@CortAmmon: "Low-Level" là tương đối, lshoặc dirlà mức khá cao cho một số loại phát triển, cũng giống như bashhoặc cmdhoặc kshhoặc bất cứ điều gì bạn thích shell là.
Sebastian Mach

1
@phresnel: Tôi chưa bao giờ nghĩ về nó theo cách đó. Đối với tôi, "cuộc gọi trực tiếp tới API hạt nhân của hệ điều hành của bạn" ở mức rất thấp. Tôi cho rằng có một quan điểm khác về vấn đề này đang lảng tránh tôi bởi vì tôi (tự nhiên) tiếp cận nó với những thành kiến ​​của riêng tôi.
Cort Ammon

5
@CortAmmon: đúng và lsở cấp độ cao hơn thế, vì đó không phải là cuộc gọi trực tiếp đến API hạt nhân của hệ điều hành của bạn. Đây là một ứng dụng (nhỏ).
Steve Jessop

1
@SteveJessop. Tôi gọi là "nhận nội dung của một thư mục" cấp thấp. Tôi không nghĩ lshay dirnhưng opendir()/readdir()(linux api) hoặc FindFirstFile()/FindNextFile()(windows api) hoặc File.listFiles(java API) hoặc Directory.GetFiles()(C #). Tất cả những thứ này được liên kết chặt chẽ với một cuộc gọi trực tiếp đến HĐH. Một số có thể đơn giản như đẩy một số vào một thanh ghi và gọi int 13hđể kích hoạt chế độ kernel.
Cort Ammon

133

Nó an toàn hơn. Để cho bạn một ý tưởng ở đây là một kịch bản ví dụ

import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)

Nếu đầu vào từ người dùng thì test; rm -rf ~điều này sẽ xóa thư mục chính.

Đây là lý do tại sao nó an toàn hơn để sử dụng chức năng tích hợp.

Do đó tại sao bạn nên sử dụng quy trình con thay vì hệ thống quá.


26
Hoặc một cách khác để xem xét nó, những gì dễ dàng hơn để làm đúng, viết chương trình Python hoặc viết chương trình Python viết kịch bản shell? :-)
Steve Jessop

3
@SteveJessop, một đồng nghiệp của tôi đã rất ngạc nhiên khi một tập lệnh Python nhỏ tôi đã giúp anh ấy viết kịch bản shell shell nhanh hơn gấp 20 (!). Tôi đã giải thích rằng chuyển hướng đầu ra có thể trông gợi cảm - nhưng nó đòi hỏi phải mở và đóng tệp trên mỗi lần lặp. Nhưng một số người thích làm công việc một cách khó khăn - :)
núi lửa

1
@SteveJessop, đây là một câu hỏi mẹo - bạn sẽ không biết cho đến khi chạy! :)

60

Có bốn trường hợp mạnh để ưu tiên các phương thức cụ thể hơn của Python trong osmô-đun hơn là sử dụng os.systemhoặc subprocessmô-đun khi thực hiện lệnh:

  • - sinh ra một quá trình khác là dư thừa và lãng phí thời gian và tài nguyên.
  • Tính di động - Nhiều phương thức trongos mô-đun có sẵn trong nhiều nền tảng trong khi nhiều lệnh shell là đặc thù của os.
  • Hiểu kết quả - Tạo ra một quy trình để thực thi các lệnh tùy ý buộc bạn phải phân tích kết quả từ đầu ra và hiểu nếutại sao một lệnh đã làm sai.
  • An toàn - Một quy trình có khả năng thực thi bất kỳ lệnh nào được đưa ra. Đây là một thiết kế yếu và nó có thể tránh được bằng cách sử dụng các phương pháp cụ thể trong osmô-đun.

Dự phòng (xem mã dự phòng ):

Bạn đang thực sự thực hiện một "người trung gian" dư thừa trên đường đến các cuộc gọi hệ thống cuối cùng (chmod trong ví dụ của bạn). Người đàn ông trung lưu này là một quá trình mới hoặc vỏ phụ.

Từ os.system:

Thực hiện lệnh (một chuỗi) trong một khung con ...

subprocess chỉ là một mô-đun để sinh ra các quy trình mới.

Bạn có thể làm những gì bạn cần mà không sinh ra các quá trình này.

Tính di động (xem tính di động của mã nguồn ):

Các osmục tiêu của Module này là để cung cấp các dịch vụ hệ điều hành chung và nó bắt đầu mô tả với:

Mô-đun này cung cấp một cách di động sử dụng chức năng phụ thuộc hệ điều hành.

Bạn có thể sử dụng os.listdirtrên cả windows và unix. Cố gắng sử dụng os.system/ subprocesscho chức năng này sẽ buộc bạn phải duy trì hai cuộc gọi (cho ls/ dir) và kiểm tra xem bạn đang sử dụng hệ điều hành nào. Đây không phải là di động và sẽ gây ra sự thất vọng nhiều hơn về sau (xem Xử lý đầu ra ).

Hiểu kết quả của lệnh:

Giả sử bạn muốn liệt kê các tập tin trong một thư mục.

Nếu bạn đang sử dụng os.system("ls")/ subprocess.call(['ls']), bạn chỉ có thể lấy lại đầu ra của quy trình, về cơ bản là một chuỗi lớn có tên tệp.

Làm thế nào bạn có thể nói một tệp có một khoảng trắng trong tên của nó từ hai tệp?

Điều gì nếu bạn không có quyền liệt kê các tập tin?

Làm thế nào bạn nên ánh xạ dữ liệu đến các đối tượng python?

Những điều này chỉ nằm ngoài đỉnh đầu của tôi, và trong khi có những giải pháp cho những vấn đề này - tại sao lại giải quyết một vấn đề đã được giải quyết cho bạn?

Đây là một ví dụ về việc tuân thủ nguyên tắc Đừng lặp lại chính mình (Thường được gọi là "DRY") bằng cách không lặp lại một triển khai đã tồn tại và có sẵn miễn phí cho bạn.

Sự an toàn:

os.systemsubprocesscó sức mạnh Thật tốt khi bạn cần sức mạnh này, nhưng nó nguy hiểm khi bạn không. Khi bạn sử dụng os.listdir, bạn biết nó không thể làm gì khác ngoài việc liệt kê các tệp hoặc đưa ra lỗi. Khi bạn sử dụng os.systemhoặcsubprocess để đạt được hành vi tương tự, bạn có khả năng sẽ làm điều gì đó mà bạn không có ý định làm.

Tiêm an toàn (xem ví dụ tiêm vỏ ) :

Nếu bạn sử dụng đầu vào từ người dùng như một lệnh mới, về cơ bản bạn đã đưa cho anh ta một vỏ. Điều này giống như SQL tiêm cung cấp shell trong DB cho người dùng.

Một ví dụ sẽ là một lệnh của mẫu:

# ... read some user input
os.system(user_input + " some continutation")

Điều này có thể dễ dàng khai thác để chạy bất kỳ mã tùy ý nào bằng cách sử dụng đầu vào: NASTY COMMAND;#để tạo cuối cùng:

os.system("NASTY COMMAND; # some continuation")

Có rất nhiều lệnh như vậy có thể khiến hệ thống của bạn gặp rủi ro.


3
Tôi sẽ nói 2. là lý do chính.
jaredad7

23

Vì một lý do đơn giản - khi bạn gọi một hàm shell, nó sẽ tạo một lớp con bị hủy sau khi lệnh của bạn tồn tại, vì vậy nếu bạn thay đổi thư mục trong shell - nó không ảnh hưởng đến môi trường của bạn trong Python.

Bên cạnh đó, việc tạo lớp vỏ phụ rất tốn thời gian, do đó, sử dụng trực tiếp các lệnh hệ điều hành sẽ ảnh hưởng đến hiệu suất của bạn

BIÊN TẬP

Tôi đã có một số bài kiểm tra thời gian chạy:

In [379]: %timeit os.chmod('Documents/recipes.txt', 0755)
10000 loops, best of 3: 215 us per loop

In [380]: %timeit os.system('chmod 0755 Documents/recipes.txt')
100 loops, best of 3: 2.47 ms per loop

In [382]: %timeit call(['chmod', '0755', 'Documents/recipes.txt'])
100 loops, best of 3: 2.93 ms per loop

Chức năng nội bộ chạy nhanh hơn 10 lần

EDIT2

Có thể có trường hợp khi gọi thực thi bên ngoài có thể mang lại kết quả tốt hơn các gói Python - Tôi chỉ nhớ một thư được gửi bởi một đồng nghiệp của tôi rằng hiệu suất của gzip được gọi thông qua quy trình con cao hơn nhiều so với hiệu suất của gói Python mà anh ta đã sử dụng. Nhưng chắc chắn không phải khi chúng ta đang nói về các gói HĐH tiêu chuẩn mô phỏng các lệnh OS chuẩn


Điều gì có thể được thực hiện với iPython? Không nghĩ rằng bạn có thể sử dụng các chức năng đặc biệt bắt đầu bằng %việc sử dụng trình thông dịch bình thường.
iProgram

@aPyDeveloper, vâng, đó là iPython - trên Ubuntu. "Magical" % timeit là một phước lành - mặc dù có một số trường hợp - chủ yếu là với định dạng chuỗi - mà nó không thể xử lý
núi lửa

1
Hoặc bạn cũng có thể tạo một kịch bản python và sau đó nhập time <path to script> vào terminal và nó sẽ cho bạn biết thời gian thực, người dùng và quá trình thực hiện. Đó là nếu bạn không có iPython và bạn có quyền truy cập vào dòng lệnh Unix.
iProgram

1
@aPyDeveloper, tôi thấy không có lý do gì để làm việc chăm chỉ - khi tôi có iPython trên máy của mình
núi lửa

Thật! Tôi đã nói nếu bạn không có iPython. :)
iProgram

16

Shell gọi là đặc thù của hệ điều hành trong khi hầu hết các trường hợp chức năng mô-đun Python thì không. Và nó tránh sinh ra một quy trình con.


1
Các chức năng mô-đun Python cũng sinh ra các quy trình con mới để gọi một lớp con mới.
Koderok

7
@Koderok vô nghĩa, các chức năng mô-đun được gọi là trong quá trình
dwurf

3
@Koderok: mô-đun os sử dụng các lệnh hệ thống cơ bản mà lệnh shell được sử dụng, nó không sử dụng các lệnh shell. Điều này có nghĩa là cuộc gọi hệ thống os thường an toàn và nhanh hơn (không phân tích chuỗi, boo fork, không thực thi, thay vào đó chỉ là lệnh gọi kernel) so với các lệnh shell. Lưu ý rằng trong hầu hết các trường hợp, lệnh gọi shell và lệnh gọi hệ thống thường có cùng tên hoặc cùng tên, nhưng có tài liệu riêng; cuộc gọi shell nằm trong man phần 1 (phần man mặc định) trong khi cuộc gọi hệ thống có tên tương đương nằm ở man phần 2 (ví dụ: man 2 chmod).
Lie Ryan

1
@ dwurf, LieRyan: xấu của tôi! Tôi đã có một quan niệm sai lầm, dường như. Cảm ơn!
Koderok

11

Nó hiệu quả hơn nhiều. "Shell" chỉ là một hệ nhị phân hệ điều hành khác chứa rất nhiều lệnh gọi hệ thống. Tại sao phải chịu chi phí chung của việc tạo toàn bộ quá trình shell chỉ cho cuộc gọi hệ thống duy nhất đó?

Tình hình thậm chí còn tồi tệ hơn khi bạn sử dụng os.systemcho một thứ không phải là vỏ tích hợp. Bạn bắt đầu một tiến trình shell, lần lượt bắt đầu một thực thi mà sau đó (hai quá trình đi) thực hiện cuộc gọi hệ thống. Ít nhất subprocesssẽ loại bỏ sự cần thiết cho một quá trình trung gian vỏ.

Nó không đặc trưng cho Python, cái này. systemdlà một sự cải tiến như vậy đối với thời gian khởi động Linux vì cùng một lý do: nó làm cho hệ thống cần thiết tự gọi nó thay vì sinh ra một ngàn vỏ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.