Câu hỏi Gọi một lệnh bên ngoài bằng Python


Làm thế nào tôi có thể gọi một lệnh bên ngoài (như thể tôi đã gõ nó vào Unix shell hoặc Windows command prompt) từ bên trong một kịch bản Python?


3635
2017-09-18 01:35


gốc




Các câu trả lời:


Nhìn vào mô đun phụ trong thư viện chuẩn:

from subprocess import call
call(["ls", "-l"])

Ưu điểm của subprocess so với hệ thống là nó linh hoạt hơn (bạn có thể nhận được stdout, stderr, mã trạng thái "thực", xử lý lỗi tốt hơn, v.v ...).

Các tài liệu chính thức đề xuất subprocess mô-đun trên os.system thay thế ():

Các subprocess mô-đun cung cấp các cơ sở mạnh mẽ hơn để tạo ra các quy trình mới và truy xuất kết quả của chúng; sử dụng mô đun đó thích hợp hơn khi sử dụng hàm này [os.system()].

Các "Thay thế các hàm cũ hơn bằng mô đun subprocess"phần trong subprocess tài liệu có thể có một số công thức nấu ăn hữu ích.

Tài liệu chính thức về subprocess module:


3504
2017-09-18 01:39



Có cách nào để sử dụng thay thế biến? IE tôi đã cố gắng làm echo $PATH bằng cách sử dụng call(["echo", "$PATH"]), nhưng nó chỉ lặp lại chuỗi chữ $PATH thay vì làm bất kỳ sự thay thế nào. Tôi biết tôi có thể nhận được biến môi trường PATH, nhưng tôi tự hỏi nếu có một cách dễ dàng để có lệnh hành xử chính xác như thể tôi đã thực hiện nó trong bash. - Kevin Wheeler
@ KevinWheeler Bạn sẽ phải sử dụng shell=True để làm việc đó. - SethMMorton
@ KevinWheeler Bạn KHÔNG nên sử dụng shell=True, với mục đích này, Python đi kèm với os.path.expandvars. Trong trường hợp của bạn, bạn có thể viết: os.path.expandvars("$PATH"). @SethMMorton vui lòng xem xét lại nhận xét của bạn -> Tại sao không sử dụng shell = True - Murmel
Theo Python 3.5, bạn nên sử dụng subprocess.run thay vì subprocess.call. docs.python.org/3/library/subprocess.html - Hannes Karppila
Các cuộc gọi mẫu ls -l nhưng không cấp quyền truy cập vào đầu ra của nó (stdout không thể truy cập được). Tôi thấy rằng khó hiểu - bạn có thể sử dụng một lệnh mà không có stdout thay thế, chẳng hạn như touch. - florisla


Dưới đây là tóm tắt các cách gọi các chương trình bên ngoài và những lợi thế và bất lợi của mỗi chương trình:

  1. os.system("some_command with args") chuyển lệnh và các đối số vào hệ vỏ của bạn. Điều này là tốt đẹp bởi vì bạn thực sự có thể chạy nhiều lệnh cùng một lúc theo cách này và thiết lập đường ống và chuyển hướng đầu vào / đầu ra. Ví dụ:

    os.system("some_command < input_file | another_command > output_file")  
    

    Tuy nhiên, trong khi điều này thuận tiện, bạn phải tự xử lý việc thoát khỏi các ký tự shell như dấu cách, vv Mặt khác, điều này cũng cho phép bạn chạy các lệnh chỉ đơn giản là các lệnh shell và không thực sự là các chương trình bên ngoài. Xem tài liệu.

  2. stream = os.popen("some_command with args") sẽ làm điều tương tự như os.system ngoại trừ việc nó cung cấp cho bạn một đối tượng giống như tệp mà bạn có thể sử dụng để truy nhập đầu vào / đầu ra tiêu chuẩn cho quy trình đó. Có 3 biến thể khác của popen mà tất cả xử lý i / o hơi khác nhau. Nếu bạn truyền tất cả mọi thứ dưới dạng một chuỗi, thì lệnh của bạn sẽ được chuyển tới trình bao; nếu bạn vượt qua chúng như một danh sách thì bạn không cần phải lo lắng về việc thoát khỏi bất cứ điều gì. Xem tài liệu.

  3. Các Popen lớp của subprocess mô-đun. Đây là mục đích thay thế cho os.popen nhưng có nhược điểm là hơi phức tạp hơn bởi đức hạnh là toàn diện. Ví dụ: bạn muốn nói:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
    

    thay vì:

    print os.popen("echo Hello World").read()
    

    nhưng nó là tốt đẹp để có tất cả các tùy chọn có trong một lớp thống nhất thay vì 4 chức năng popen khác nhau. Xem tài liệu.

  4. Các call chức năng từ subprocess mô-đun. Điều này về cơ bản giống như Popen lớp và nhận tất cả các đối số giống nhau, nhưng nó chỉ đơn giản chờ cho đến khi lệnh hoàn tất và cung cấp cho bạn mã trả về. Ví dụ:

    return_code = subprocess.call("echo Hello World", shell=True)  
    

    Xem tài liệu.

  5. Nếu bạn đang sử dụng Python 3.5 trở lên, bạn có thể sử dụng subprocess.run chức năng, giống như ở trên nhưng thậm chí còn linh hoạt hơn và trả về CompletedProcess khi lệnh kết thúc thực hiện.

  6. Mô-đun os cũng có tất cả các hàm fork / exec / spawn mà bạn có trong chương trình C, nhưng tôi không khuyên bạn nên sử dụng chúng trực tiếp.

Các subprocess mô-đun có lẽ nên là những gì bạn sử dụng.

Cuối cùng hãy lưu ý rằng đối với tất cả các phương thức mà bạn truyền lệnh cuối cùng sẽ được thực thi bởi trình bao dưới dạng một chuỗi và bạn có trách nhiệm thoát khỏi nó. Có những tác động bảo mật nghiêm trọng nếu bất kỳ phần nào của chuỗi mà bạn vượt qua không thể tin cậy hoàn toàn. Ví dụ: nếu người dùng nhập một số phần bất kỳ của chuỗi. Nếu bạn không chắc chắn, chỉ sử dụng các phương thức này với hằng số. Để cung cấp cho bạn một gợi ý về các hàm ý, hãy xem xét mã này:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

và tưởng tượng rằng người dùng nhập "mama của tôi không yêu tôi && rm-rf /".


2469
2017-09-18 13:11



Câu trả lời hay / giải thích tốt. Câu trả lời này biện minh cho phương châm của Python như được mô tả trong bài viết này như thế nào? fastcompany.com/3026446/…  "Phong cách, Perl và Python có những triết lý khác nhau. Các khẩu hiệu nổi tiếng nhất của Perl là" Có nhiều cách để làm điều đó ". Python được thiết kế để có một cách rõ ràng để làm điều đó" Có vẻ như nó là một cách khác! Trong Perl, tôi biết chỉ có hai cách để thực hiện một lệnh - sử dụng đánh dấu hoặc open. - Jean
Nếu sử dụng Python 3.5+, hãy sử dụng subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run - phoenix
Cái mà người ta thường biết là những gì được thực hiện với STDOUT và STDOUT của tiến trình con, vì nếu chúng bị bỏ qua, trong một số điều kiện (khá phổ biến), cuối cùng tiến trình con sẽ phát ra lời gọi hệ thống để ghi vào STDOUT (STDERR nữa?) mà sẽ vượt quá bộ đệm đầu ra được cung cấp cho quá trình của hệ điều hành, và hệ điều hành sẽ làm cho nó chặn cho đến khi một số tiến trình đọc từ bộ đệm đó. Vì vậy, với các cách được đề xuất hiện tại, subprocess.run(..), chính xác thì sao "Điều này không nắm bắt stdout hoặc stderr theo mặc định." bao hàm, ngụ ý? Thế còn subprocess.check_output(..) và STDERR? - Evgeni Sergeev
lệnh nào bạn đã đề xuất chặn tập lệnh của tôi? tức là nếu tôi muốn chạy nhiều lệnh trong một for vòng lặp như thế nào để tôi làm điều đó mà không có nó chặn kịch bản python của tôi? Tôi không quan tâm đến đầu ra của lệnh tôi chỉ muốn chạy nhiều lệnh. - Charlie Parker
@ phoenix Tôi không đồng ý. Không có gì ngăn cản bạn sử dụng os.system trong python3 docs.python.org/3/library/os.html#os.system - Qback


Tôi thường sử dụng:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

Bạn được tự do làm những gì bạn muốn với stdout dữ liệu trong đường ống. Thực tế, bạn có thể chỉ cần bỏ qua các tham số đó (stdout= và stderr=) và nó sẽ hoạt động như os.system().


257
2017-09-18 18:20



.readlines() lần đọc tất cả các các dòng cùng một lúc tức là, nó chặn cho đến khi thoát khỏi quy trình con (đóng phần cuối của đường ống). Để đọc trong thời gian thực (nếu không có vấn đề về bộ đệm), bạn có thể: for line in iter(p.stdout.readline, ''): print line, - jfs
Bạn có thể giải thích về ý nghĩa của bạn bằng "nếu không có vấn đề về bộ đệm" không? Nếu quá trình chặn chắc chắn, cuộc gọi subprocess cũng chặn. Điều tương tự cũng có thể xảy ra với ví dụ ban đầu của tôi. Điều gì khác có thể xảy ra liên quan đến đệm? - EmmEff
quá trình con có thể sử dụng tính năng chặn khối ở chế độ không tương tác thay vì xếp theo bộ đệm p.stdout.readline() (lưu ý: không s ở cuối) sẽ không thấy bất kỳ dữ liệu nào cho đến khi trẻ lấp đầy bộ đệm của nó. Nếu đứa trẻ không tạo ra nhiều dữ liệu thì đầu ra sẽ không theo thời gian thực. Xem lý do thứ hai trong Q: Tại sao không chỉ sử dụng một đường ống (popen ())?. Một số cách giải quyết được cung cấp trong câu trả lời này (pexpect, pty, stdbuf) - jfs
vấn đề đệm chỉ quan trọng nếu bạn muốn đầu ra trong thời gian thực và không áp dụng cho mã của bạn mà không in bất cứ điều gì cho đến khi tất cả các dữ liệu được nhận - jfs


Một số gợi ý về việc tách quy trình con khỏi quá trình gọi (bắt đầu quá trình con trong nền).

Giả sử bạn muốn bắt đầu một nhiệm vụ dài từ một CGI-script, đó là quá trình con nên sống lâu hơn quá trình thực thi CGI-script.

Ví dụ cổ điển từ các tài liệu mô-đun subprocess là:

import subprocess
import sys

# some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess

# some more code here

Ý tưởng ở đây là bạn không muốn chờ trong dòng 'gọi subprocess' cho đến khi longtask.py được hoàn thành. Nhưng nó không rõ ràng những gì xảy ra sau khi dòng 'một số mã ở đây' từ ví dụ.

Nền tảng mục tiêu của tôi là freebsd, nhưng sự phát triển đã được trên cửa sổ, vì vậy tôi phải đối mặt với vấn đề trên cửa sổ đầu tiên.

Trên các cửa sổ (win xp), tiến trình cha sẽ không kết thúc cho đến khi longtask.py hoàn thành công việc của nó. Nó không phải là những gì bạn muốn trong CGI-script. Vấn đề không cụ thể đối với Python, trong cộng đồng PHP, các vấn đề đều giống nhau.

Giải pháp là vượt qua DETACHED_PROCESS Cờ tạo quy trình vào hàm CreateProcess cơ bản trong win API. Nếu bạn tình cờ đã cài đặt pywin32, bạn có thể nhập cờ từ mô-đun win32process, nếu không bạn nên tự định nghĩa nó:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksun trong nhận xét bên dưới ghi chú rằng cờ chính xác về mặt ngữ nghĩa là CREATE_NEW_CONSOLE (0x00000010) * /

Trên freebsd chúng ta có một vấn đề khác: khi quá trình cha mẹ kết thúc, nó cũng kết thúc quá trình con. Và đó không phải là điều bạn muốn trong CGI-script. Một số thí nghiệm cho thấy rằng vấn đề dường như trong việc chia sẻ sys.stdout. Và giải pháp làm việc như sau:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

Tôi đã không kiểm tra mã trên các nền tảng khác và không biết lý do của hành vi trên freebsd. Nếu có ai biết, hãy chia sẻ ý tưởng của bạn. Googling trên các quá trình nền bắt đầu bằng Python không làm giảm bất kỳ ánh sáng nào.


158
2018-02-12 10:15



tôi nhận thấy có thể "quirk" với việc phát triển các ứng dụng py2exe trong pydev + eclipse. tôi đã có thể nói rằng kịch bản chính đã không được tách ra vì cửa sổ đầu ra của eclipse không chấm dứt; ngay cả khi kịch bản thực hiện để hoàn thành nó vẫn đang chờ trả về. nhưng, khi tôi đã cố gắng biên dịch một tệp thực thi py2exe, hành vi mong đợi xảy ra (chạy các tiến trình được tách ra, sau đó thoát). tôi không chắc chắn, nhưng tên thực thi không có trong danh sách quá trình nữa. điều này làm việc cho tất cả các phương pháp (os.system ("start *"), os.spawnl với os.P_DETACH, subprocs, v.v.) - maranas
Windows gotcha: mặc dù tôi đã sinh ra quy trình với DETACHED_PROCESS, khi tôi giết trình nền Python của tôi tất cả các cổng được mở bởi nó sẽ không miễn phí cho đến khi tất cả các quá trình sinh sản chấm dứt. WScript.Shell đã giải quyết mọi vấn đề của tôi. Ví dụ ở đây: pastebin.com/xGmuvwSx - Alexey Lebedev
bạn cũng có thể cần cờ CREATE_NEW_PROCESS_GROUP. Xem Popen chờ đợi quá trình con ngay cả khi đứa trẻ ngay lập tức đã chấm dứt - jfs
Sau đây là không chính xác: "[o] n cửa sổ (win xp), quá trình cha mẹ sẽ không kết thúc cho đến khi longtask.py đã hoàn thành công việc của mình". Bố mẹ sẽ thoát bình thường, nhưng cửa sổ giao diện điều khiển (cá thể conhost.exe) chỉ đóng khi quá trình đính kèm cuối cùng thoát ra, và đứa trẻ có thể đã kế thừa giao diện điều khiển của cha mẹ. Cài đặt DETACHED_PROCESS trong creationflags tránh điều này bằng cách ngăn chặn trẻ thừa hưởng hoặc tạo bảng điều khiển. Thay vào đó, nếu bạn muốn một bảng điều khiển mới, hãy sử dụng CREATE_NEW_CONSOLE (0x00000010). - eryksun
Tôi không có nghĩa rằng thực hiện như là một quá trình tách rời là không chính xác. Điều đó nói rằng, bạn có thể cần phải thiết lập các tiêu chuẩn xử lý các tập tin, đường ống, hoặc os.devnull vì một số chương trình điều khiển thoát với lỗi khác. Tạo bảng điều khiển mới khi bạn muốn quá trình con tương tác với người dùng đồng thời với quy trình gốc. Nó sẽ gây nhầm lẫn khi cố gắng làm cả hai trong một cửa sổ duy nhất. - eryksun


Tôi muốn khuyên bạn nên sử dụng mô-đun subprocess thay vì os.system vì nó shell thoát cho bạn và do đó an toàn hơn nhiều: http://docs.python.org/library/subprocess.html

subprocess.call(['ping', 'localhost'])

98
2017-09-18 01:42





import os
cmd = 'ls -al'
os.system(cmd)

Nếu bạn muốn trả về kết quả của lệnh, bạn có thể sử dụng os.popen. Tuy nhiên, điều này không được chấp nhận vì phiên bản 2.6 có lợi cho mô đun phụ, các câu trả lời khác đã được đề cập đến.


94
2017-09-18 01:37



popen không được chấp nhận ủng hộ subprocess. - Fox Wilson
Bạn cũng có thể lưu kết quả của bạn với cuộc gọi os.system, vì nó hoạt động giống như vỏ UNIX, ví dụ như os.system ('ls -l> test2.txt') - Stefan Gruenwald


import os
os.system("your command")

Lưu ý rằng điều này là nguy hiểm, vì lệnh này không được làm sạch. Tôi để lại cho bạn để google cho các tài liệu có liên quan trên các mô-đun 'os' và 'sys'. Có một loạt các hàm (exec * và spawn *) sẽ làm những việc tương tự.


84
2017-09-18 01:37



Ý của bạn là gì "lệnh không được làm sạch"? - Peter Mortensen
Không có ý tưởng những gì tôi có nghĩa là gần một thập kỷ trước (kiểm tra ngày!), Nhưng nếu tôi đã đoán, nó sẽ được rằng không có xác nhận thực hiện. - nimish


Tôi luôn sử dụng fabric cho những thứ này như:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

Nhưng điều này dường như là một công cụ tốt: sh (Giao diện con của Python).

Xem ví dụ:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

52
2018-03-13 00:12