Chạy lệnh shell và bắt đầu ra


908

Tôi muốn viết một hàm sẽ thực thi lệnh shell và trả lại đầu ra của nó dưới dạng một chuỗi , bất kể đó là lỗi hay thông báo thành công. Tôi chỉ muốn nhận được kết quả tương tự mà tôi đã nhận được với dòng lệnh.

Điều gì sẽ là một ví dụ mã sẽ làm một điều như vậy?

Ví dụ:

def run_command(cmd):
    # ??????

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'

Câu trả lời:


1138

Câu trả lời cho câu hỏi này phụ thuộc vào phiên bản Python bạn đang sử dụng. Cách tiếp cận đơn giản nhất là sử dụng subprocess.check_outputhàm:

>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

check_outputchạy một chương trình duy nhất chỉ lấy các đối số làm đầu vào. 1 Nó trả về kết quả chính xác như được in stdout. Nếu bạn cần viết đầu vào stdin, bỏ qua phần trước runhoặc Popenphần. Nếu bạn muốn thực thi các lệnh shell phức tạp, hãy xem ghi chú shell=Trueở cuối câu trả lời này.

Các check_outputchức năng hoạt động trên hầu hết các phiên bản của Python vẫn sử dụng rộng rãi (2.7+). 2 Nhưng đối với các phiên bản gần đây hơn, nó không còn là phương pháp được đề xuất.

Các phiên bản hiện đại của Python (3,5 trở lên): run

Nếu bạn đang sử dụng Python 3.5 trở lên và không cần khả năng tương thích ngược , chức năng mớirun được khuyến nghị. Nó cung cấp một API cấp cao, rất chung cho subprocessmô-đun. Để nắm bắt đầu ra của một chương trình, chuyển subprocess.PIPEcờ cho stdoutđối số từ khóa. Sau đó truy cập stdoutthuộc tính của CompletedProcessđối tượng trả về :

>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Giá trị trả về là một bytesđối tượng, vì vậy nếu bạn muốn có một chuỗi thích hợp, bạn sẽ cần đến decodenó. Giả sử quy trình được gọi trả về chuỗi được mã hóa UTF-8:

>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Tất cả điều này có thể được nén thành một lớp lót:

>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Nếu bạn muốn chuyển đầu vào cho tiến trình stdin, hãy chuyển một bytesđối tượng cho inputđối số từ khóa:

>>> cmd = ['awk', 'length($0) > 5']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'

Bạn có thể chụp lỗi bằng cách chuyển stderr=subprocess.PIPE(chụp đến result.stderr) hoặc stderr=subprocess.STDOUT(chụp result.stdouttheo cùng với đầu ra thông thường). Khi bảo mật không phải là vấn đề đáng lo ngại, bạn cũng có thể chạy các lệnh shell phức tạp hơn bằng cách chuyển shell=Truenhư mô tả trong các ghi chú bên dưới.

Điều này chỉ thêm một chút phức tạp, so với cách làm cũ. Nhưng tôi nghĩ rằng nó đáng để trả tiền: bây giờ bạn có thể làm hầu hết mọi thứ bạn cần làm với runchức năng một mình.

Các phiên bản cũ hơn của Python (2.7-3.4): check_output

Nếu bạn đang sử dụng phiên bản cũ hơn của Python hoặc cần khả năng tương thích ngược khiêm tốn, có lẽ bạn có thể sử dụng check_outputchức năng như được mô tả ngắn gọn ở trên. Nó đã có sẵn từ Python 2.7.

subprocess.check_output(*popenargs, **kwargs)  

Nó nhận các đối số tương tự như Popen(xem bên dưới) và trả về một chuỗi chứa đầu ra của chương trình. Phần đầu của câu trả lời này có một ví dụ sử dụng chi tiết hơn. Trong Python 3.5 trở lên, check_outputtương đương với việc thực thi runvới check=Truestdout=PIPEvà chỉ trả về stdoutthuộc tính.

Bạn có thể vượt qua stderr=subprocess.STDOUTđể đảm bảo rằng các thông báo lỗi được bao gồm trong đầu ra được trả về - nhưng trong một số phiên bản Python chuyển stderr=subprocess.PIPEđến check_outputcó thể gây ra bế tắc . Khi bảo mật không phải là vấn đề đáng lo ngại, bạn cũng có thể chạy các lệnh shell phức tạp hơn bằng cách chuyển shell=Truenhư mô tả trong các ghi chú bên dưới.

Nếu bạn cần chuyển từ stderrhoặc chuyển đầu vào cho quy trình, check_outputsẽ không hoàn thành nhiệm vụ. Xem các Popenví dụ dưới đây trong trường hợp đó.

Các ứng dụng phức tạp và các phiên bản kế thừa của Python (2.6 trở xuống): Popen

Nếu bạn cần khả năng tương thích ngược sâu hoặc nếu bạn cần chức năng phức tạp hơn check_outputcung cấp, bạn sẽ phải làm việc trực tiếp với Popencác đối tượng, đóng gói API cấp thấp cho các quy trình con.

Hàm Popentạo chấp nhận một lệnh đơn không có đối số hoặc danh sách chứa lệnh là mục đầu tiên của nó, theo sau là bất kỳ số lượng đối số nào, mỗi đối số là một mục riêng biệt trong danh sách. shlex.splitcó thể giúp phân tích chuỗi thành các danh sách được định dạng thích hợp. Popencác đối tượng cũng chấp nhận một loạt các đối số khác nhau để quản lý IO quá trình và cấu hình mức thấp.

Để gửi đầu vào và đầu ra chụp, communicatehầu như luôn luôn là phương thức ưa thích. Như trong:

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

Hoặc là

>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo

Nếu bạn đặt stdin=PIPE, communicatecũng cho phép bạn chuyển dữ liệu tới quy trình thông qua stdin:

>>> cmd = ['awk', 'length($0) > 5']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
...                           stderr=subprocess.PIPE,
...                           stdin=subprocess.PIPE)
>>> out, err = p.communicate('foo\nfoofoo\n')
>>> print out
foofoo

Lưu ý câu trả lời Aaron Hall , mà chỉ ra rằng trên một số hệ thống, bạn có thể cần phải thiết lập stdout, stderrstdintất cả mọi người PIPE(hoặc DEVNULL) để có được communicateđể làm việc ở tất cả.

Trong một số trường hợp hiếm hoi, bạn có thể cần chụp đầu ra thời gian thực phức tạp. Câu trả lời của Vartec cho thấy một con đường phía trước, nhưng các phương pháp khác hơn communicatelà dễ bị bế tắc nếu không được sử dụng cẩn thận.

Như với tất cả các chức năng trên, khi bảo mật không phải là vấn đề đáng lo ngại, bạn có thể chạy các lệnh shell phức tạp hơn bằng cách chuyển shell=True.

Ghi chú

1. Chạy lệnh shell: shell=Trueđối số

Thông thường, mỗi cuộc gọi đến run, check_outputhoặc các Popennhà xây dựng thực hiện một chương trình đơn lẻ . Điều đó có nghĩa là không có đường ống kiểu bash ưa thích. Nếu bạn muốn chạy các lệnh shell phức tạp, bạn có thể vượt qua shell=True, cả ba chức năng đều hỗ trợ.

Tuy nhiên, làm như vậy làm tăng mối quan tâm an ninh . Nếu bạn đang làm bất cứ điều gì ngoài kịch bản nhẹ, bạn nên gọi riêng từng quy trình và chuyển đầu ra từ mỗi quy trình sang đầu vào tiếp theo, thông qua

run(cmd, [stdout=etc...], input=other_output)

Hoặc là

Popen(cmd, [stdout=etc...]).communicate(other_output)

Sự cám dỗ để kết nối trực tiếp các đường ống là mạnh mẽ; chống lại nó. Nếu không, bạn có thể sẽ thấy bế tắc hoặc phải làm những việc như thế này .

2. Cân nhắc về Unicode

check_outputtrả về một chuỗi trong Python 2, nhưng một bytesđối tượng trong Python 3. Thật đáng để dành một chút thời gian để tìm hiểu về unicode nếu bạn chưa có.


5
Cả với check_output()communicate()bạn phải đợi cho đến khi quá trình hoàn tất, với việc poll()bạn sẽ nhận được đầu ra. Thực sự phụ thuộc những gì bạn cần.
vartec

2
Không chắc chắn nếu điều này chỉ áp dụng cho các phiên bản sau của Python, nhưng biến outlà loại <class 'bytes'>dành cho tôi. Để có được đầu ra dưới dạng một chuỗi, tôi phải giải mã nó trước khi in như sau:out.decode("utf-8")
PolyMesh

1
@par Điều đó không hiệu quả với bạn khi bạn vượt qua shell=True? Nó làm việc cho tôi. Bạn không cần shlex.splitkhi bạn vượt qua shell=True. shlex.splitdành cho các lệnh không shell. Tôi nghĩ rằng tôi sẽ lấy ra một chút bởi vì điều này đang làm vẩn đục nước.
gửi

2
Python 3.5+ cho phép một đối số từ khóa universal_newlines=Truecho phép bạn truyền vào và lấy ra các chuỗi Unicode trong mã hóa mặc định của hệ thống. Trong 3.7 điều này đã được đổi tên thành hợp lý hơn text=True.
tripleee

2
Đối với Python 3.6+, bạn có thể sử dụng encodingtham số subprocess.runthay vì sử dụng result.stdout.decode('utf-8'), bạn có thể sử dụng subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8').
Pierre

191

Đây là cách dễ dàng hơn, nhưng chỉ hoạt động trên Unix (bao gồm cả Cygwin) và Python2.7.

import commands
print commands.getstatusoutput('wc -l file')

Nó trả về một tuple với (return_value, đầu ra).

Đối với một giải pháp hoạt động trong cả Python2 và Python3, hãy sử dụng subprocessmô-đun thay thế:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response

31
Hiện không được chấp nhận, nhưng rất hữu ích cho các phiên bản python cũ mà không có quy trình
con.checkDefput

22
Lưu ý rằng đây là dành riêng cho Unix. Ví dụ, nó sẽ thất bại trên Windows.
Zitrax

4
+1 Tôi phải làm việc trên phiên bản cổ của python 2.4 và điều này RẤT hữu ích
javadba

1
Anh chàng PIPE là gì xuất hiện hiển thị mã đầy đủ: quy trình con.PIPE
Kyle Bridenstine

@KyleBridenstine bạn có thể chỉnh sửa câu trả lời.
Boris

106

Một cái gì đó như thế:

def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while(True):
        # returns None while subprocess is running
        retcode = p.poll() 
        line = p.stdout.readline()
        yield line
        if retcode is not None:
            break

Lưu ý rằng tôi đang chuyển hướng stderr sang thiết bị xuất chuẩn, nó có thể không chính xác như bạn muốn, nhưng tôi cũng muốn thông báo lỗi.

Hàm này mang lại từng dòng một khi chúng đến (thông thường bạn sẽ phải đợi quá trình con hoàn thành để có được đầu ra như một tổng thể).

Đối với trường hợp của bạn, việc sử dụng sẽ là:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,

Hãy chắc chắn thực hiện một số loại vòng lặp hoạt động để có được đầu ra để tránh sự bế tắc tiềm ẩn trong waitvà các callchức năng.
André Caron

@Silver Light: quá trình của bạn có lẽ đang chờ đầu vào từ người dùng. Hãy thử cung cấp một PIPEgiá trị cho stdinvà đóng tệp đó ngay khi Popentrả về.
André Caron

4
-1: đó là một vòng lặp vô hạn nếu retcode0. Việc kiểm tra nên được if retcode is not None. Bạn không nên tạo ra các chuỗi trống (ngay cả một dòng trống cũng có ít nhất một ký hiệu '\ n') : if line: yield line. Gọi p.stdout.close()ở cuối.
jfs

2
Tôi đã thử mã với ls -l / dirname và nó bị hỏng sau khi liệt kê hai tệp trong khi có nhiều tệp hơn trong thư mục
Vasilis

3
@fuenfundachtzig: .readlines()sẽ không quay trở lại cho đến khi tất cả đầu ra được đọc và do đó nó bị hỏng vì đầu ra lớn không phù hợp với bộ nhớ. Ngoài ra, để tránh mất dữ liệu đệm sau khi thoát khỏi quy trình con, nên có một tương tựif retcode is not None: yield from p.stdout.readlines(); break
jfs 21/12/13

67

Câu trả lời của Vartec không đọc tất cả các dòng, vì vậy tôi đã tạo một phiên bản:

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

Cách sử dụng giống như câu trả lời được chấp nhận:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)

6
bạn có thể sử dụng return iter(p.stdout.readline, b'')thay vì vòng lặp while
jfs

2
Đó là một cách sử dụng khá hay của iter, không biết điều đó! Tôi đã cập nhật mã.
Tối đa Ekman

Tôi khá chắc chắn thiết bị xuất chuẩn giữ tất cả đầu ra, đó là một đối tượng luồng với bộ đệm. Tôi sử dụng một kỹ thuật rất giống nhau để làm cạn kiệt tất cả đầu ra còn lại sau khi Popen đã hoàn thành và trong trường hợp của tôi, sử dụng poll () và readline trong khi thực hiện để bắt đầu ra trực tiếp.
Max Ekman

Tôi đã xóa bình luận sai lệch của tôi. Tôi có thể xác nhận, p.stdout.readline()có thể trả về đầu ra không đệm trước đó ngay cả khi tiến trình con đã thoát ( p.poll()không None).
jfs

Mã này không hoạt động. Xem tại đây stackoverflow.com/questions/24340877/ từ
thang

61

Đây là một giải pháp phức tạp nhưng siêu đơn giản , hoạt động trong nhiều tình huống:

import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()

Một tệp tạm thời (ở đây là tmp) được tạo với đầu ra của lệnh và bạn có thể đọc từ đó đầu ra mong muốn của bạn.

Ghi chú thêm từ các bình luận: Bạn có thể xóa tệp tmp trong trường hợp công việc một lần. Nếu bạn cần làm điều này nhiều lần, không cần phải xóa tmp.

os.remove('tmp')

5
Hacky nhưng siêu đơn giản + hoạt động ở bất cứ đâu .. có thể kết hợp nó mktempđể làm cho nó hoạt động trong các tình huống theo luồng tôi đoán
Prakash Rajagaopal

2
Có thể là phương pháp nhanh nhất, nhưng tốt hơn là thêm os.remove('tmp')vào để làm cho nó "không bị bẩn".
XuMuK

@XuMuK Bạn đúng trong trường hợp làm việc một lần. Nếu đó là một công việc lặp đi lặp lại có lẽ việc xóa là không cần thiết
Mehdi Saman Booy

1
xấu cho đồng thời, xấu cho các chức năng reentrant, xấu khi không rời khỏi hệ thống như trước khi nó bắt đầu (không dọn dẹp)
2mia

1
@ 2mia Rõ ràng là dễ dàng vì một lý do! Nếu bạn muốn sử dụng tệp như một loại bộ nhớ dùng chung để đọc và ghi đồng thời, đây không phải là một lựa chọn tốt. Nhưng, cho s.th. như có đầu ra của một lệnh (ví dụ: ls hoặc find hoặc ...) nó có thể là một lựa chọn tốt và nhanh chóng. Btw nếu bạn cần một giải pháp nhanh chóng cho một vấn đề đơn giản thì đó là điều tốt nhất tôi nghĩ. Nếu bạn cần một đường ống, quy trình con hoạt động hiệu quả hơn.
Mehdi Saman Booy

44

Tôi có cùng một vấn đề nhưng đã tìm ra một cách rất đơn giản để làm điều này:

import subprocess
output = subprocess.getoutput("ls -l")
print(output)

Hy vọng nó sẽ giúp

Lưu ý: Giải pháp này dành riêng cho Python3 vì subprocess.getoutput()không hoạt động trong Python2


Làm thế nào để giải quyết vấn đề của OP? Xin hãy giải thích.
RamenChef

4
Nó trả về đầu ra của lệnh dưới dạng chuỗi, đơn giản như vậy
azhar22k

1
Tất nhiên, print là một tuyên bố trên Python 2. Bạn sẽ có thể nhận ra đây là câu trả lời của Python 3.

2
@Dev print (s) là python hợp lệ 2. sub process.getoutput thì không.
dùng48956

2
Đối với hầu hết các trường hợp sử dụng, đây là những gì mọi người có thể muốn: dễ nhớ, không phải giải mã kết quả, v.v ... Cảm ơn bạn.
bwv549

18

Bạn có thể sử dụng các lệnh sau để chạy bất kỳ lệnh shell nào. Tôi đã sử dụng chúng trên Ubuntu.

import os
os.popen('your command here').read()

Lưu ý: Điều này không được chấp nhận kể từ python 2.6. Bây giờ bạn phải sử dụng subprocess.Popen. Dưới đây là ví dụ

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")

2
Không dùng nữa kể từ phiên bản 2.6 - docs.python.org/2/l Library / os.html
Filippo Vitale

1
@FilippoVitale Cảm ơn. Tôi không biết rằng nó bị phản đối.
Muhammad Hassan

1
Theo raspberrypi.stackexchange.com/questions/71547/, os.popen() bị phản đối trong Python 2.6, nhưng nó không bị phản đối trong Python 3.x, vì trong 3.x nó được triển khai bằng cách sử dụng subprocess.Popen().
JL

12

Mileage của bạn có thể khác nhau, tôi đã thử quay vòng của @ senderle trên giải pháp của Vartec trong Windows trên Python 2.6.5, nhưng tôi đã gặp lỗi và không có giải pháp nào khác hoạt động. Lỗi của tôi là : WindowsError: [Error 6] The handle is invalid.

Tôi thấy rằng tôi phải gán PIPE cho mọi tay cầm để có được nó để trả lại đầu ra mà tôi mong đợi - cách sau đây hiệu quả với tôi.

import subprocess

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    return subprocess.Popen(cmd, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE).communicate()

và gọi như thế này, ( [0]lấy phần tử đầu tiên của bộ dữ liệu, stdout):

run_command('tracert 11.1.0.1')[0]

Sau khi tìm hiểu thêm, tôi tin rằng tôi cần những đối số đường ống này bởi vì tôi đang làm việc trên một hệ thống tùy chỉnh sử dụng các tay cầm khác nhau, vì vậy tôi phải trực tiếp kiểm soát tất cả các tiêu chuẩn.

Để dừng cửa sổ bật lên bảng điều khiển (với Windows), hãy làm điều này:

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    # instantiate a startupinfo obj:
    startupinfo = subprocess.STARTUPINFO()
    # set the use show window flag, might make conditional on being in Windows:
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    # pass as the startupinfo keyword argument:
    return subprocess.Popen(cmd,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            startupinfo=startupinfo).communicate()

run_command('tracert 11.1.0.1')

1
Thú vị - đây phải là một điều Windows. Tôi sẽ thêm một lưu ý chỉ ra điều này trong trường hợp mọi người đang gặp lỗi tương tự.
gửi

sử dụng DEVNULLthay vì của subprocess.PIPEnếu bạn không viết / đọc từ một ống nếu không bạn có thể treo các tiến trình con.
jfs

10

Tôi có một hương vị hơi khác nhau của cùng một vấn đề với các yêu cầu sau:

  1. Chụp và trả lại tin nhắn STDOUT khi chúng tích lũy trong bộ đệm STDOUT (tức là trong thời gian thực).
    • @vartec đã giải quyết vấn đề này bằng cách sử dụng máy phát điện và
      từ khóa 'suất' ở trên
  2. In tất cả các dòng STDOUT ( ngay cả khi quá trình thoát ra trước khi bộ đệm STDOUT có thể được đọc đầy đủ )
  3. Đừng lãng phí chu kỳ CPU làm ô nhiễm quá trình ở tần số cao
  4. Kiểm tra mã trả về của quy trình con
  5. In STDERR (tách biệt với STDOUT) nếu chúng tôi nhận được mã trả về lỗi khác không.

Tôi đã kết hợp và điều chỉnh các câu trả lời trước đó để đưa ra các câu trả lời sau:

import subprocess
from time import sleep

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         shell=True)
    # Read stdout from subprocess until the buffer is empty !
    for line in iter(p.stdout.readline, b''):
        if line: # Don't print blank lines
            yield line
    # This ensures the process has completed, AND sets the 'returncode' attr
    while p.poll() is None:                                                                                                                                        
        sleep(.1) #Don't waste CPU-cycles
    # Empty STDERR buffer
    err = p.stderr.read()
    if p.returncode != 0:
       # The run_command() function is responsible for logging STDERR 
       print("Error: " + str(err))

Mã này sẽ được thực hiện giống như các câu trả lời trước:

for line in run_command(cmd):
    print(line)

1
Bạn có phiền giải thích việc bổ sung giấc ngủ (.1) sẽ không lãng phí chu kỳ CPU không?
Moataz Elmasry

2
Nếu chúng tôi tiếp tục gọi p.poll()mà không có bất kỳ giấc ngủ nào giữa các cuộc gọi, chúng tôi sẽ lãng phí chu kỳ CPU bằng cách gọi chức năng này hàng triệu lần. Thay vào đó, chúng tôi "điều tiết" vòng lặp của mình bằng cách nói với HĐH rằng chúng tôi không cần phải bận tâm trong 1/10 giây tiếp theo, để nó có thể thực hiện các nhiệm vụ khác. (Có thể là p.poll () cũng ngủ, khiến cho tuyên bố về giấc ngủ của chúng ta trở nên dư thừa).
Aelfinn

5

Việc tách lệnh ban đầu subprocesscó thể khó và cồng kềnh.

Sử dụng shlex.split()để giúp mình ra ngoài.

Lệnh mẫu

git log -n 5 --since "5 years ago" --until "2 year ago"

Mật mã

from subprocess import check_output
from shlex import split

res = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

Không có shlex.split()mã sẽ trông như sau

res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    '5 years ago', 
    '--until', 
    '2 year ago'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

1
shlex.split()là một sự tiện lợi, đặc biệt nếu bạn không biết cách trích dẫn chính xác trong trình bao; nhưng thủ công chuyển đổi chuỗi này vào danh sách ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']không khó chút nào nếu bạn hiểu trích dẫn.
tripleee

4

Nếu bạn cần chạy lệnh shell trên nhiều tệp, thì đây là mẹo cho tôi.

import os
import subprocess

# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec's solution because it wasn't printing all lines)
def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

# Get all filenames in working directory
for filename in os.listdir('./'):
    # This command will be run on each file
    cmd = 'nm ' + filename

    # Run the command and capture the output line by line.
    for line in runProcess(cmd.split()):
        # Eliminate leading and trailing whitespace
        line.strip()
        # Split the output 
        output = line.split()

        # Filter the output and print relevant lines
        if len(output) > 2:
            if ((output[2] == 'set_program_name')):
                print filename
                print line

Chỉnh sửa: Chỉ cần xem giải pháp của Max Persson với đề xuất của JF Sebastian. Đã đi trước và kết hợp điều đó.


Popenchấp nhận một chuỗi, nhưng sau đó bạn cần shell=Truehoặc một danh sách các đối số, trong trường hợp đó bạn nên truyền vào ['nm', filename]thay vì một chuỗi. Loại thứ hai là thích hợp hơn vì vỏ thêm phức tạp mà không cung cấp bất kỳ giá trị nào ở đây. Vượt qua một chuỗi mà không shell=Truerõ ràng xảy ra để làm việc trên Windows, nhưng điều đó có thể thay đổi trong bất kỳ phiên bản Python tiếp theo nào.
tripleee

2

Theo @senderle, nếu bạn sử dụng python3.6 như tôi:

def sh(cmd, input=""):
    rst = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))
    assert rst.returncode == 0, rst.stderr.decode("utf-8")
    return rst.stdout.decode("utf-8")
sh("ls -a")

Sẽ hành động chính xác như bạn chạy lệnh trong bash


Bạn đang phát minh lại các đối số từ khóa check=True, universal_newlines=True. Nói cách khác, subprocess.run()đã làm mọi thứ mà mã của bạn làm.
tripleee

1

Nếu bạn sử dụng subprocessmô-đun python, bạn có thể xử lý STDOUT, STDERR và trả lại mã lệnh riêng. Bạn có thể xem một ví dụ để thực hiện lệnh gọi hoàn chỉnh. Tất nhiên bạn có thể mở rộng nó với try..exceptnếu bạn muốn.

Hàm bên dưới trả về mã STDOUT, STDERR và Return để bạn có thể xử lý chúng trong tập lệnh khác.

import subprocess

def command_caller(command=None)
    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False)
    out, err = sp.communicate()
    if sp.returncode:
        print(
            "Return code: %(ret_code)s Error message: %(err_msg)s"
            % {"ret_code": sp.returncode, "err_msg": err}
            )
    return sp.returncode, out, err

Một sự tái hiện nghèo nàn khác của subprocess.run(). Đừng phát minh lại bánh xe.
tripleee

0

ví dụ: thực thi ('ls -ahl') phân biệt ba / bốn lợi nhuận có thể và nền tảng hệ điều hành:

  1. không có đầu ra, nhưng chạy thành công
  2. đầu ra dòng trống, chạy thành công
  3. chạy thất bại
  4. xuất một cái gì đó, chạy thành công

chức năng dưới đây

def execute(cmd, output=True, DEBUG_MODE=False):
"""Executes a bash command.
(cmd, output=True)
output: whether print shell output to screen, only affects screen display, does not affect returned values
return: ...regardless of output=True/False...
        returns shell output as a list with each elment is a line of string (whitespace stripped both sides) from output
        could be 
        [], ie, len()=0 --> no output;    
        [''] --> output empty line;     
        None --> error occured, see below

        if error ocurs, returns None (ie, is None), print out the error message to screen
"""
if not DEBUG_MODE:
    print "Command: " + cmd

    # https://stackoverflow.com/a/40139101/2292993
    def _execute_cmd(cmd):
        if os.name == 'nt' or platform.system() == 'Windows':
            # set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        else:
            # Use bash; the default is sh
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable="/bin/bash")

        # the Popen() instance starts running once instantiated (??)
        # additionally, communicate(), or poll() and wait process to terminate
        # communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as tuple
        # if communicate(), the results are buffered in memory

        # Read stdout from subprocess until the buffer is empty !
        # if error occurs, the stdout is '', which means the below loop is essentially skipped
        # A prefix of 'b' or 'B' is ignored in Python 2; 
        # it indicates that the literal should become a bytes literal in Python 3 
        # (e.g. when code is automatically converted with 2to3).
        # return iter(p.stdout.readline, b'')
        for line in iter(p.stdout.readline, b''):
            # # Windows has \r\n, Unix has \n, Old mac has \r
            # if line not in ['','\n','\r','\r\n']: # Don't print blank lines
                yield line
        while p.poll() is None:                                                                                                                                        
            sleep(.1) #Don't waste CPU-cycles
        # Empty STDERR buffer
        err = p.stderr.read()
        if p.returncode != 0:
            # responsible for logging STDERR 
            print("Error: " + str(err))
            yield None

    out = []
    for line in _execute_cmd(cmd):
        # error did not occur earlier
        if line is not None:
            # trailing comma to avoid a newline (by print itself) being printed
            if output: print line,
            out.append(line.strip())
        else:
            # error occured earlier
            out = None
    return out
else:
    print "Simulation! The command is " + cmd
    print ""

0

Đầu ra có thể được chuyển hướng đến một tệp văn bản và sau đó đọc lại.

import subprocess
import os
import tempfile

def execute_to_file(command):
    """
    This function execute the command
    and pass its output to a tempfile then read it back
    It is usefull for process that deploy child process
    """
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    path = temp_file.name
    command = command + " > " + path
    proc = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if proc.stderr:
        # if command failed return
        os.unlink(path)
        return
    with open(path, 'r') as f:
        data = f.read()
    os.unlink(path)
    return data

if __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))

Chắc chắn nó có thể, nhưng tại sao bạn muốn; và tại sao bạn sẽ sử dụng vỏ thay vì vượt qua stdout=temp_file?
tripleee

Trên thực tế, nói chung bạn đúng nhưng trong ví dụ của tôi ecls.exedường như triển khai một công cụ dòng lệnh khác, vì vậy cách đơn giản đôi khi không hoạt động.
MR

0

chỉ cần viết một tập lệnh bash nhỏ để làm điều này bằng cách sử dụng curl

https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5

#!/usr/bin/env bash

# Usage: gdrive_dl.sh <url>

urlBase='https://drive.google.com'
fCookie=tmpcookies

curl="curl -L -b $fCookie -c $fCookie"
confirm(){
    $curl "$1" | grep jfk-button-action | sed -e 's/.*jfk-button-action" href="\(\S*\)".*/\1/' -e 's/\&amp;/\&/g'
}

$curl -O -J "${urlBase}$(confirm $1)"
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.