Эквивалент обратных кавычек Bash в Python [дубликат]

85

Что эквивалентно обратным кавычкам в Ruby и Perl в Python? То есть в Ruby я могу это сделать:

foo = `cat /tmp/baz`

Как эквивалентный оператор выглядит в Python? Я пробовал, os.system("cat /tmp/baz")но это приводит к стандартному результату и возвращает мне код ошибки этой операции.

Крис Банч
источник

Ответы:

101
output = os.popen('cat /tmp/baz').read()
Джон Кугельман
источник
4
@mckenzm Вопрос заключается в захвате вывода внешнего процесса. Захват вывода функции Python - это совсем другой вопрос.
Джон Кугельман 02
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
да, это единственный разумный способ, вы могли бы обернуть его в функцию, чтобы вы могли вызывать что-то вроде execute («команда»)
Винко Врсалович
На самом деле это не работает для меня, так как в данном случае baz - это каталог, и я пытаюсь получить содержимое всех файлов в этом каталоге. (выполнение cat / tmp / baz / * работает в тиках, но не с помощью метода, описанного здесь)
Крис Банч
6
re: "*" не работает; используйте вместо этого subprocess.Popen (["cat", "/ tmp / baz"], stdout = subprocess.PIPE, shell = True). Поскольку расширение glob (звездочка) обрабатывается оболочкой, модуль подпроцессинга должен в этом случае использовать расширение оболочки (предоставляемое / bin / sh).
Паси Саволайнен,
1
Из docs.python.org/2/library/subprocess.html#popen-constructor : "(с shell = True) Если args является последовательностью, первый элемент указывает командную строку, и любые дополнительные элементы будут рассматриваться как дополнительные аргументы к самой оболочке ". Итак, если вы собираетесь использовать shell = True, то первым аргументом, вероятно, должна быть строка «cat / tmp / baz». В качестве альтернативы, если вы хотите использовать последовательность в качестве первого аргумента, вы должны использовать shell = False
onlynone
1
@gerrit: он не устарел. Документы рекомендуют subprocess.run() (я не знаю, заслужено ли это), если вам не нужно поддерживать более ранние версии или если вам не нужна гибкость, обеспечиваемая Popen().
jfs
27

что правильно . Вы также можете использовать 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"])
jfs
источник
3
Теперь это самый простой способ. "subprocess.check_output" был добавлен в Python 2.7, выпущенный в июле 2010 года, после того, как были даны другие "всплывающие" ответы.
Роберт Флеминг
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
6

Самый простой способ - использовать пакет команд.

import commands

commands.getoutput("whoami")

Вывод:

'bganesan'

Балачандер Ганесан
источник
6
Очень просто, но этот модуль устарел.
Gringo Suave
3
import os
foo = os.popen('cat /tmp/baz', 'r').read()
awatts
источник
3
Это эквивалент обратных кавычек Ruby, но если ваша проблема состоит в том, чтобы вывести список содержимого каталога, то это не лучший способ сделать это.
awatts
2

я использую

(6: 0) $ python - версия 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. Посмотрев на строку документа для подпроцесса, я заменил

["прога", "аргумент"]

с участием

"прог арг"

и получил желаемое поведение расширения оболочки (а-ля Perl `prog 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