Ý nghĩa thực tế của 'shell = True' trong quy trình con


260

Tôi đang gọi các quá trình khác nhau với các subprocessmô-đun. Tuy nhiên, tôi có một câu hỏi.

Trong các mã sau:

callProcess = subprocess.Popen(['ls', '-l'], shell=True)

callProcess = subprocess.Popen(['ls', '-l']) # without shell

Cả hai đều làm việc. Sau khi đọc các tài liệu, tôi biết rằng điều đó shell=Truecó nghĩa là thực thi mã thông qua shell. Vì vậy, điều đó có nghĩa là vắng mặt, quá trình được bắt đầu trực tiếp.

Vì vậy, những gì tôi nên thích cho trường hợp của tôi - tôi cần phải chạy một quy trình và nhận đầu ra của nó. Tôi có lợi ích gì khi gọi nó từ bên trong vỏ hoặc bên ngoài nó.


21
lệnh đầu tiên không chính xác: -lđược truyền cho /bin/sh(shell) thay vì lschương trình trên Unix nếushell=True . Đối số chuỗi nên được sử dụng với shell=Truetrong hầu hết các trường hợp thay vì danh sách.
jfs

1
lại "quá trình được bắt đầu trực tiếp": Wut?
mã allyour

9
Câu nói "Cả hai đều hoạt động." về 2 cuộc gọi đó là không chính xác và sai lệch. Các cuộc gọi hoạt động khác nhau. Chỉ cần chuyển từ shell=Truesang Falsevà ngược lại là một lỗi. Từ tài liệu : "Trên POSIX có shell = True, (...) Nếu args là một chuỗi, mục đầu tiên chỉ định chuỗi lệnh và bất kỳ mục bổ sung nào sẽ được coi là đối số bổ sung cho chính shell.". Trên Windows có chuyển đổi tự động , có thể không mong muốn.
mbdevpl

Câu trả lời:


183

Lợi ích của việc không gọi qua shell là bạn không gọi 'chương trình bí ẩn'. Trên POSIX, biến môi trường SHELLkiểm soát nhị phân nào được gọi là "shell". Trên Windows, không có hậu duệ vỏ bourne, chỉ có cmd.exe.

Vì vậy, việc gọi shell sẽ gọi một chương trình do người dùng lựa chọn và phụ thuộc vào nền tảng. Nói chung, tránh các yêu cầu thông qua vỏ.

Gọi qua shell không cho phép bạn mở rộng các biến môi trường và tập tin ảm đạm theo cơ chế thông thường của shell. Trên các hệ thống POSIX, shell sẽ mở rộng các tập tin thành một danh sách các tập tin. Trên Windows, dù sao đi nữa, một tệp toàn cầu (ví dụ: "*. *") Không được mở rộng bằng shell (nhưng các biến môi trường trên một dòng lệnh được mở rộng bởi cmd.exe).

Nếu bạn nghĩ rằng bạn muốn mở rộng biến môi trường và tập tin ảm đạm, hãy nghiên cứu các ILScuộc tấn công năm 1992 trên các dịch vụ mạng thực hiện các yêu cầu chương trình con thông qua trình bao. Ví dụ bao gồm các sendmailcửa hậu khác nhau liên quan ILS.

Tóm lại, sử dụng shell=False.


2
Cảm ơn câu trả lời. Mặc dù tôi thực sự không ở giai đoạn mà tôi nên lo lắng về việc khai thác, nhưng tôi hiểu những gì bạn đang nhận được.
user225312

55
Nếu bạn bất cẩn ngay từ đầu, không có gì phải lo lắng sẽ giúp bạn bắt kịp sau này. ;)
Heath Hunnicutt

Điều gì nếu bạn muốn giới hạn bộ nhớ tối đa của quy trình con? stackoverflow.com/questions/3172470/
hy

8
tuyên bố về $SHELLlà không chính xác. Để trích dẫn sub process.html: "Trên Unix với shell=True, shell mặc định là /bin/sh." (không $SHELL)
marcin

1
@ user2428107: Có, nếu bạn sử dụng lệnh gọi backtick trên Perl, bạn đang sử dụng lệnh gọi shell và mở ra các vấn đề tương tự. Sử dụng 3+ arg opennếu bạn muốn các cách an toàn để gọi một chương trình và nắm bắt đầu ra.
ShadowRanger

137
>>> import subprocess
>>> subprocess.call('echo $HOME')
Traceback (most recent call last):
...
OSError: [Errno 2] No such file or directory
>>>
>>> subprocess.call('echo $HOME', shell=True)
/user/khong
0

Đặt đối số shell thành giá trị thực sẽ khiến quy trình con sinh ra một quy trình shell trung gian và bảo nó chạy lệnh. Nói cách khác, sử dụng shell trung gian có nghĩa là các biến, mẫu hình cầu và các tính năng shell đặc biệt khác trong chuỗi lệnh được xử lý trước khi lệnh được chạy. Ở đây, trong ví dụ, $ HOME đã được xử lý trước lệnh echo. Trên thực tế, đây là trường hợp lệnh với mở rộng shell trong khi lệnh ls -l được coi là một lệnh đơn giản.

nguồn: Mô đun quy trình


16
Không biết tại sao đây không phải là câu trả lời được chọn. Cho đến nay, câu hỏi thực sự phù hợp với câu hỏi
Rodrigo Lopez Guerra

1
đồng ý. đây là một ví dụ tốt cho tôi để hiểu shell = True nghĩa là gì.
dùng389955

2
Đặt đối số shell thành giá trị thực sẽ khiến quy trình con sinh ra một quy trình shell trung gian và bảo nó chạy lệnh Oh chúa ơi điều này nói lên tất cả. Tại sao câu trả lời này không được chấp nhận ??? tại sao?
pouya

Tôi nghĩ rằng vấn đề là đối số đầu tiên để gọi là một danh sách, không phải là một chuỗi, nhưng điều đó sẽ gây ra lỗi nếu shell là Sai. Thay đổi lệnh thành một danh sách sẽ làm cho công việc này
Lincoln Randall McFarland

Xin lỗi bình luận trước của tôi đã đi trước khi tôi được thực hiện. Để rõ ràng: Tôi thường thấy quy trình con sử dụng với shell = True và lệnh là một chuỗi, ví dụ: 'ls -l', (tôi hy vọng sẽ tránh được lỗi này) nhưng quy trình con lấy một danh sách (và một chuỗi làm danh sách một thành phần) . Để chạy ra ngoài gọi shell (và các vấn đề bảo mật với điều đó ), hãy sử dụng một danh sách sub process.call (['ls', '-l'])
Lincoln Randall McFarland

42

Một ví dụ về những điều có thể sai với Shell = True được hiển thị ở đây

>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
What file would you like to display?
non_existent; rm -rf / # THIS WILL DELETE EVERYTHING IN ROOT PARTITION!!!
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...

Kiểm tra tài liệu ở đây: sub process.call ()


6
Các liên kết là rất hữu ích. Như liên kết đã nêu: Thực thi các lệnh shell kết hợp đầu vào không được xác thực từ một nguồn không tin cậy làm cho chương trình dễ bị tiêm shell, một lỗ hổng bảo mật nghiêm trọng có thể dẫn đến thực thi lệnh tùy ý. Vì lý do này, việc sử dụng shell = True không được khuyến khích mạnh mẽ trong trường hợp chuỗi lệnh được xây dựng từ đầu vào bên ngoài.
jtuki

39

Thực thi các chương trình thông qua shell có nghĩa là tất cả đầu vào của người dùng được truyền vào chương trình được diễn giải theo cú pháp và quy tắc ngữ nghĩa của shell được gọi. Tốt nhất, điều này chỉ gây ra sự bất tiện cho người dùng, vì người dùng phải tuân theo các quy tắc này. Chẳng hạn, các đường dẫn chứa các ký tự shell đặc biệt như dấu ngoặc kép hoặc khoảng trắng phải được thoát. Tệ nhất, nó gây rò rỉ bảo mật, bởi vì người dùng có thể thực thi các chương trình tùy ý.

shell=Trueđôi khi thuận tiện để sử dụng các tính năng shell cụ thể như tách từ hoặc mở rộng tham số. Tuy nhiên, nếu tính năng này là bắt buộc, hãy sử dụng các mô-đun khác được cung cấp cho bạn (ví dụ: os.path.expandvars()để mở rộng tham số hoặc shlexđể tách từ). Điều này có nghĩa là nhiều công việc hơn, nhưng tránh các vấn đề khác.

Tóm lại: Tránh shell=Truebằng mọi cách.


16

Các câu trả lời khác ở đây giải thích thỏa đáng các cảnh báo an ninh cũng được đề cập trong subprocesstài liệu. Nhưng ngoài ra, chi phí khởi động chương trình để bắt đầu chương trình bạn muốn chạy thường không cần thiết và chắc chắn là ngớ ngẩn đối với các tình huống mà bạn không thực sự sử dụng bất kỳ chức năng nào của trình bao. Hơn nữa, sự phức tạp ẩn thêm sẽ làm bạn sợ, đặc biệt là nếu bạn không quen thuộc với trình bao hoặc các dịch vụ mà nó cung cấp.

Trong đó các tương tác với shell là không cần thiết, bây giờ bạn yêu cầu người đọc và người duy trì tập lệnh Python (có thể là hoặc không phải là bản thân tương lai của bạn) để hiểu cả Python và shell script. Ghi nhớ phương châm Python "rõ ràng là tốt hơn ngầm định"; ngay cả khi mã Python sẽ phức tạp hơn một chút so với tập lệnh shell tương đương (và thường rất ngắn gọn), bạn có thể nên loại bỏ shell và thay thế chức năng bằng các cấu trúc Python nguyên gốc. Giảm thiểu công việc được thực hiện trong một quy trình bên ngoài và giữ quyền kiểm soát trong mã của bạn càng nhiều càng tốt thường là một ý tưởng tốt đơn giản vì nó giúp cải thiện khả năng hiển thị và giảm rủi ro của các tác dụng phụ mong muốn hoặc không mong muốn.

Mở rộng ký tự đại diện, nội suy biến đổi và chuyển hướng đều đơn giản để thay thế bằng các cấu trúc Python nguyên gốc. Một đường ống vỏ phức tạp nơi các bộ phận hoặc tất cả không thể được viết lại một cách hợp lý trong Python sẽ là một tình huống mà có lẽ bạn có thể cân nhắc sử dụng trình bao. Bạn vẫn nên đảm bảo rằng bạn hiểu hiệu suất và ý nghĩa bảo mật.

Trong trường hợp tầm thường, để tránh shell=True, chỉ cần thay thế

subprocess.Popen("command -with -options 'like this' and\\ an\\ argument", shell=True)

với

subprocess.Popen(['command', '-with','-options', 'like this', 'and an argument'])

Lưu ý cách đối số đầu tiên là một danh sách các chuỗi cần chuyển đến execvp()và cách trích dẫn các chuỗi siêu ký tự shell thoát dấu gạch chéo thường không cần thiết (hoặc hữu ích hoặc chính xác). Có lẽ cũng thấy Khi nào bọc dấu ngoặc kép quanh một biến shell?

Như một bên, bạn rất muốn tránh Popennếu một trong những trình bao bọc đơn giản hơn trong subprocessgói làm những gì bạn muốn. Nếu bạn có một Python đủ gần đây, có lẽ bạn nên sử dụng subprocess.run.

  • Với check=True nó sẽ thất bại nếu lệnh bạn chạy thất bại.
  • Với stdout=subprocess.PIPE nó sẽ nắm bắt đầu ra của lệnh.
  • Hơi khó hiểu, với universal_newlines=Truenó sẽ giải mã đầu ra thành một chuỗi Unicode thích hợp (nó chỉ bytesnằm trong mã hóa hệ thống, trên Python 3).

Nếu không, đối với nhiều tác vụ, bạn muốn check_outputlấy đầu ra từ một lệnh, trong khi kiểm tra xem nó có thành công hay khôngcheck_call chưa có đầu ra để thu thập.

Tôi sẽ kết thúc bằng một câu trích dẫn của David Korn: "Viết shell dễ dàng hơn so với kịch bản shell di động." Thậm chí subprocess.run('echo "$HOME"', shell=True)không thể mang theo Windows.


Tôi nghĩ rằng trích dẫn là từ Larry Wall nhưng Google nói với tôi khác.
tripleee 15/03/2016

Đó là một cuộc nói chuyện cao - nhưng không có đề xuất kỹ thuật nào để thay thế: Ở đây, trên OS-X, tôi đang cố gắng mua lại ứng dụng Mac mà tôi đã khởi chạy thông qua 'open': process = sub process.Popen ('/ usr / bin / pgrep - n '+ app_name, shell = false, stdout = sub process.PIPE, stderr = sub process.PIPE) app_pid, err = process.cransicate () --- nhưng nó không hoạt động trừ khi tôi sẽ sử dụng shell = True. Giờ thì sao?
Motti Shneor

Có rất nhiều câu hỏi về cách tránh shell=True, nhiều câu trả lời xuất sắc. Bạn tình cờ chọn một trong đó là về lý do tại sao thay thế.
tripleee

@MottiShneor Cảm ơn bạn đã phản hồi; thêm ví dụ đơn giản
tripleee 03/03/2016

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.