Еквівалент Bash Backticks у Python [дублікат]


84

Що є еквівалентом зворотних посилань, знайдених у Ruby та Perl у Python? Тобто в Ruby я можу це зробити:

foo = `cat /tmp/baz`

Як виглядає еквівалентне твердження в Python? Я спробував, os.system("cat /tmp/baz")але це виводить результат на стандартне значення і повертає мені код помилки цієї операції.


Відповіді:


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

4
@mckenzm Питання полягає в захопленні результатів зовнішнього процесу. Захоплення результату роботи функції Python було б зовсім іншим питанням.
Джон Кугельман

1
Чудово лаконічний і справді еквівалент Ruby `...`(захоплення stdout, проходження stderr) - за одним винятком: Ruby дозволяє визначити код виходу процесу через $?факт; у Python, з того, що я можу сказати, вам доведеться використовувати для цього subprocessфункції модуля.
mklement0,

82

Найбільш гнучким способом є використання subprocessмодуля:

import subprocess

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

capture_outputбула введена в Python 3.7, для старих версій check_output()замість цього можна використовувати спеціальну функцію :

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

Ви також можете вручну створити об'єкт підпроцесу, якщо вам потрібен тонкий контроль:

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

Усі ці функції підтримують параметри ключових слів, щоб налаштувати, як саме виконується підпроцес. Наприклад, ви можете використовувати shell=Trueдля запуску програму через оболонку, якщо вам потрібні такі речі, як розширення імен файлів *, але це має обмеження .


2
так, це єдиний розумний спосіб, ви можете обернути його у функцію, щоб ви могли викликати щось на зразок виконання ("команда")
Вінко Врсалович,

Це насправді не працює для мене, оскільки в цьому випадку baz - це каталог, і я намагаюся отримати вміст усіх файлів у цьому каталозі. (робити cat / tmp / baz / * працює у кліщах, але не за методом, описаним тут)
Кріс Банч,

6
re: "*" не працює; використовуйте замість subprocess.Popen (["cat", "/ tmp / baz"], stdout = subprocess.PIPE, shell = True). Оскільки розширення глобуса (зірки) обробляється оболонкою, модуль підроблення повинен використовувати розширення оболонки в цьому випадку (надається / bin / sh).
Пасі Саволайнен,

1
З docs.python.org/2/library/subprocess.html#popen-constructor : "(with shell = True) Якщо args є послідовністю, перший елемент задає рядок команди, а будь-які додаткові елементи будуть розглядатися як додаткові аргументи до самої оболонки ". Отже, якщо ви збираєтеся використовувати shell = True, тоді першим аргументом, ймовірно, повинен бути рядок "cat / tmp / baz". Як варіант, якщо ви хочете використовувати послідовність як перший аргумент, вам слід використовувати shell = False
onlynone

1
@gerrit: це не застаріло. Документи рекомендують subprocess.run() (я не знаю, чи заслуговує це), якщо вам не потрібно підтримувати попередні версії або якщо вам не потрібна гнучкість, яку надає Popen().
jfs

27

sth правильно . Ви також можете використовувати os.popen (), але там, де він доступний (Python 2.4+), як правило, кращий підпроцес.

Однак, на відміну від деяких мов, які заохочують це, зазвичай вважається поганою формою створення підпроцесу, де ви можете виконувати ту ж роботу всередині мови. Це повільніше, менш надійно і залежить від платформи. Ваш приклад буде краще, як:

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

ета:

baz - це каталог, і я намагаюся отримати вміст усіх файлів у цьому каталозі

? cat у каталозі отримує помилку.

Якщо вам потрібен список файлів:

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

Якщо ви хочете вміст усіх файлів у каталозі, щось на зразок:

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)

або, якщо ви можете бути впевнені, що там немає каталогів, ви можете помістити його в однорядковий вкладиш:

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

1
Хоча це не відповідь на запитання, це найкраща відповідь для навчання користувачів.
noamtm

1
Заголовок питання "що є еквівалентом зворотних позначок". Я припустив, що "кішка" - це лише приклад команди. Ця відповідь не допомагає в загальному випадку.
Джейсон

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

3
Зараз це найпростіший спосіб. "subprocess.check_output" було додано до Python 2.7, який був випущений в липні 2010 року, після того, як були надані інші відповіді "popen".
Роберт Флемінг,

10

Починаючи з Python 3.5, рекомендованим способом є використання subprocess.run. Оскільки Python 3.7, щоб отримати таку ж поведінку, як ви описуєте, ви використовуєте:

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

Це поверне subprocess.CompletedProcessоб’єкт. Вихід до stdout буде в cpe.stdout, вихід до stderr буде cpe.stderr, що буде обома bytesоб'єктами. Ви можете декодувати результат , щоб отримати strоб'єкт за допомогою cpe.stdout.decode()або отримати пропускання text=Trueдо subprocess.run:

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

В останньому випадку cpe.stdoutі cpe.stderrє обома strоб'єктами.


З python 3.7 можна використовувати text=Trueпараметр для повернення str замість байтів.
bwv549,


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

3
Це еквівалент зворотних міток Ruby, але якщо ваша проблема полягає у переліку вмісту каталогу, це не найкращий спосіб це зробити.
awatts

2

Я використовую

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

Одним із наведених вище прикладів є:

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

Для мене це не вдалося отримати доступ до каталогу / tmp. Після перегляду рядка документа для підпроцесу, який я замінив

["prog", "arg"]

з

"прог арг"

і отримав бажану поведінку розширення оболонки (а-ля Perl's `arg`)

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


Я кинув користуватися python на деякий час тому, що мене дратувало труднощі зробити еквівалент perl `cmd ...`. Я радий, що Python зробив це розумним.


1

Якщо ви використовуєте subprocess.Popen, не забудьте вказати bufsize. Типовим значенням є 0, що означає "не буферизований", а не "вибрати розумний за замовчуванням".


1

Це не буде працювати в python3, але в python2 ви можете продовжити strза допомогою спеціального __repr__методу, який викликає вашу команду оболонки і повертає її так:

#!/usr/bin/env python

import os

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

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

Якими ви можете користуватися як

#!/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
Також ви, мабуть, не повинні цього робити *
ThorSummoner

-1

repr()

Оператор backtick(`) був видалений у Python 3. Це заплутано схоже на одну цитату і важко набирати текст на деяких клавіатурах. Замість backtick, використовуйте еквівалентну вбудовану функцію repr().


Це не еквівалент зворотних міток bash.
gerrit
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.