Tương đương với Bash Backticks trong Python [bản sao]


84

Tương đương với các backticks được tìm thấy trong Ruby và Perl trong Python là gì? Đó là, trong Ruby, tôi có thể làm điều này:

foo = `cat /tmp/baz`

Câu lệnh tương đương trông như thế nào trong Python? Tôi đã thử os.system("cat /tmp/baz")nhưng điều đó khiến kết quả không chuẩn và trả về cho tôi mã lỗi của thao tác đó.


Câu trả lời:


100
output = os.popen('cat /tmp/baz').read()

4
@mckenzm Câu hỏi là về việc nắm bắt đầu ra của một quy trình bên ngoài. Nắm bắt đầu ra của một hàm Python sẽ là một câu hỏi hoàn toàn khác.
John Kugelman

1
`...`Ngắn gọn tuyệt vời, và thực sự tương đương với Ruby (bắt stdout, passsing stderr qua) - với một ngoại lệ: Ruby cho phép xác định mã thoát của quy trình thông qua $?sau thực tế; bằng Python, từ những gì tôi có thể nói, bạn sẽ phải sử dụng các subprocesschức năng của mô-đun cho việc đó.
mklement0

82

Cách linh hoạt nhất là sử dụng subprocessmô-đun:

import subprocess

out = subprocess.run(["cat", "/tmp/baz"], capture_output=True)
print("program output:", out)

capture_outputđã được giới thiệu trong Python 3.7, đối với các phiên bản cũ hơn, hàm đặc biệt check_output()có thể được sử dụng thay thế:

out = subprocess.check_output(["cat", "/tmp/baz"])

Bạn cũng có thể xây dựng một đối tượng quy trình con theo cách thủ công nếu bạn cần kiểm soát chi tiết:

proc = subprocess.Popen(["cat", "/tmp/baz"], stdout=subprocess.PIPE)
(out, err) = proc.communicate()

Tất cả các hàm này đều hỗ trợ các tham số từ khóa để tùy chỉnh cách thực thi chính xác của quy trình con. Ví dụ, bạn có thể sử dụng shell=Trueđể thực thi chương trình thông qua trình bao, nếu bạn cần những thứ như mở rộng tên tệp *, nhưng điều đó đi kèm với các hạn chế .


2
vâng, đây là cách duy nhất lành mạnh, bạn có thể bọc nó trong một chức năng để bạn có thể gọi một cái gì đó giống như thực hiện ( "lệnh")
Vinko Vrsalovic

Điều này thực sự không hiệu quả đối với tôi, vì trong trường hợp này, baz là một thư mục và tôi đang cố gắng lấy nội dung của tất cả các tệp trong thư mục đó. (làm mèo / tmp / baz / * tác phẩm trong ve nhưng không thông qua phương pháp này được mô tả ở đây)
Chris Bunch

6
re: "*" không hoạt động; thay vào đó hãy sử dụng subprocess.Popen (["cat", "/ tmp / baz"], stdout = subprocess.PIPE, shell = True). Vì mở rộng hình cầu (sao) được xử lý bởi trình bao, mô-đun xử lý con phải sử dụng mở rộng trình bao trong trường hợp này (được cung cấp bởi / bin / sh).
Pasi Savolainen 11/09/09

1
Từ docs.python.org/2/library/subprocess.html#popen-constructor : "(với shell = True) Nếu args là một chuỗi, mục đầu tiên chỉ định chuỗi lệnh và mọi mục bổ sung sẽ được coi là đối số bổ sung đối với chính nó. " Vì vậy, nếu bạn định sử dụng shell = True, thì đối số đầu tiên có lẽ phải là chuỗi "cat / tmp / baz". Ngoài ra, nếu bạn muốn sử dụng một chuỗi như arg đầu tiên sau đó bạn nên sử dụng vỏ = False
onlynone

1
@gerrit: nó không bị phản đối. Tài liệu khuyến nghị subprocess.run() (tôi không biết liệu nó có xứng đáng hay không) nếu bạn không cần hỗ trợ các phiên bản trước đó hoặc nếu bạn không cần sự linh hoạt được cung cấp bởi Popen().
jfs

27

sth đúng . Bạn cũng có thể sử dụng os.popen (), nhưng nếu có sẵn thì quy trình con (Python 2.4+) thường được ưu tiên hơn.

Tuy nhiên, không giống như một số ngôn ngữ khuyến khích nó, nó thường được coi là hình thức xấu để sinh ra một quy trình con, nơi bạn có thể thực hiện công việc tương tự bên trong ngôn ngữ. Nó chậm hơn, kém tin cậy hơn và phụ thuộc vào nền tảng. Ví dụ của bạn sẽ tốt hơn là:

foo= open('/tmp/baz').read()

eta:

baz là một thư mục và tôi đang cố lấy nội dung của tất cả các tệp trong thư mục đó

? con mèo trên một thư mục khiến tôi gặp lỗi.

Nếu bạn muốn có một danh sách các tệp:

import os
foo= os.listdir('/tmp/baz')

Nếu bạn muốn nội dung của tất cả các tệp trong một thư mục, như sau:

contents= []
for leaf in os.listdir('/tmp/baz'):
    path= os.path.join('/tmp/baz', leaf)
    if os.path.isfile(path):
        contents.append(open(path, 'rb').read())
foo= ''.join(contents)

hoặc, nếu bạn có thể chắc chắn rằng không có thư mục nào trong đó, bạn có thể đặt nó trong một lớp lót:

path= '/tmp/baz'
foo= ''.join(open(os.path.join(path, child), 'rb').read() for child in os.listdir(path))

1
Mặc dù đây không phải là câu trả lời cho câu hỏi, nhưng đó là câu trả lời tốt nhất để giáo dục người dùng.
noamtm

1
Tiêu đề câu hỏi là "tương đương với backticks là gì". Tôi giả định rằng "cat" chỉ là một lệnh ví dụ. Câu trả lời này không giúp ích gì cho trường hợp chung.
Jason

15
foo = subprocess.check_output(["cat", "/tmp/baz"])

3
Đây là cách đơn giản nhất bây giờ. "subprocess.check_output" đã được thêm vào Python 2.7, được phát hành vào tháng 7 năm 2010, sau khi các câu trả lời "popen" khác được đưa ra.
Robert Fleming

10

Từ Python 3.5 trở đi, cách được khuyến nghị là sử dụng subprocess.run. Kể từ Python 3.7, để có được hành vi giống như bạn mô tả, bạn sẽ sử dụng:

cpe = subprocess.run("ls", shell=True, capture_output=True)

Điều này sẽ trả về một subprocess.CompletedProcessđối tượng. Đầu ra cho stdout sẽ ở trong cpe.stdout, đầu ra cho stderr sẽ ở trong cpe.stderr, cả hai đều sẽ là bytesđối tượng. Bạn có thể giải mã đầu ra để lấy một strđối tượng bằng cách sử dụng cpe.stdout.decode()hoặc lấy một bằng cách chuyển text=Trueđến subprocess.run:

cpe = subprocess.run("ls", shell=True, capture_output=True, text=True)

Trong trường hợp thứ hai, cpe.stdoutcpe.stderrlà cả hai strđối tượng.


Từ python 3.7 trở đi, bạn có thể sử dụng text=Truetham số để lấy lại một str thay vì byte.
bwv549

6

Cách dễ nhất là sử dụng gói lệnh.

import commands

commands.getoutput("whoami")

Đầu ra:

'bganesan'


6
Rất dễ dàng, nhưng mô-đun hiện không được dùng nữa.
Gringo Suave

3
import os
foo = os.popen('cat /tmp/baz', 'r').read()

3
Điều này tương đương với các backticks của Ruby, nhưng nếu vấn đề của bạn là liệt kê nội dung của một thư mục thì đây không phải là cách tốt nhất để làm điều đó.
chờ

2

Tôi đang sử dụng

(6: 0) $ python --version Python 2.7.1

Một trong những ví dụ trên là:

import subprocess
proc = subprocess.Popen(["cat", "/tmp/baz"], stdout=subprocess.PIPE, shell=True)
(out, err) = proc.communicate()
print "program output:", out

Đối với tôi, điều này không thể truy cập vào thư mục / tmp. Sau khi xem chuỗi doc cho quy trình con, tôi đã thay thế

["prog", "arg"]

với

"prog arg"

và có được hành vi mở rộng trình bao như mong muốn (`` prog arg '' của la Perl)

print subprocess.Popen ("ls -ld / tmp / v *", stdout = subprocess.PIPE, shell = True) .communicate () [0]


Tôi đã bỏ sử dụng python một thời gian trở lại vì tôi khó chịu với việc khó thực hiện tương đương với perl `cmd ... '. Tôi rất vui khi thấy Python đã làm cho điều này hợp lý.


1

Nếu bạn sử dụng subprocess.Popen, hãy nhớ chỉ định bufsize. Mặc định là 0, có nghĩa là "không có bộ đệm", không phải "chọn một mặc định hợp lý".


1

Điều này sẽ không hoạt động trong python3, nhưng trong python2 bạn có thể mở rộng strbằng một __repr__phương thức tùy chỉnh gọi lệnh shell của bạn và trả về nó như sau:

#!/usr/bin/env python

import os

class Command(str):
    """Call system commands"""

    def __repr__(cmd):
        return os.popen(cmd).read()

Cái nào bạn có thể sử dụng như

#!/usr/bin/env python
from command import Command

who_i_am = `Command('whoami')`

# Or predeclare your shell command strings
whoami = Command('whoami')
who_i_am = `whoami`

4
Ngoài ra bạn nên có lẽ không làm * này
ThorSummoner

-1

repr()

Các backtick( `) điều hành đã được gỡ bỏ trong Python 3. Nó tương tự một cách khó hiểu với một câu trích dẫn đơn lẻ và khó gõ trên một số bàn phím. Thay vì backtick, hãy sử dụng hàm tích hợp sẵn tương đương repr().


Điều đó không tương đương với backticks bash.
gerrit
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.