Запуск команды оболочки и захват выходных данных
Я хочу написать функцию, которая выполнит команду оболочки и вернет ее выходные данные в виде строки, независимо от того, является ли это сообщением об ошибке или успешном завершении. Я просто хочу получить тот же результат, который я получил бы с помощью командной строки.
Каким был бы пример кода, который бы делал такую вещь?
Например:
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'
Переведено автоматически
Ответ 1
Во всех официально поддерживаемых версиях Python простейшим подходом является использование subprocess.check_output
функции:
>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
check_output
запускает единственную программу, которая принимает в качестве входных данных только аргументы.1 Возвращает результат в точности таким, в каком он был напечатан в stdout
. Если вам нужно записать входные данные в stdin
, переходите к разделам run
или Popen
. Если вы хотите выполнять сложные команды оболочки, смотрите Примечание к shell=True
в конце этого ответа.
Функция check_output
работает во всех официально поддерживаемых версиях Python. Но для более поздних версий доступен более гибкий подход.
Современные версии Python (3.5 или выше): run
Если вы используете Python 3.5+ и вам не нужна обратная совместимость, новая run
функция рекомендована официальной документацией для большинства задач. Он предоставляет очень общий высокоуровневый API для subprocess
модуля. Чтобы получить выходные данные программы, передайте subprocess.PIPE
флаг в stdout
аргумент ключевого слова. Затем получите доступ к stdout
атрибуту возвращаемого CompletedProcess
объекта:
>>> 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'
Возвращаемое значение является bytes
объектом, поэтому, если вам нужна соответствующая строка, вам понадобится decode
она. Предполагая, что вызываемый процесс возвращает строку в кодировке UTF-8.:
>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
При желании все это можно сжать до однострочного:
>>> 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'
Если вы хотите передать входные данные процессу stdin
, вы можете передать bytes
объект в input
аргумент ключевого слова:
>>> cmd = ['awk', 'length($0) > 5']
>>> ip = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=ip)
>>> result.stdout.decode('utf-8')
'foofoo\n'
Вы можете фиксировать ошибки, передавая stderr=subprocess.PIPE
(захват в result.stderr
) или stderr=subprocess.STDOUT
(захват в result.stdout
вместе с обычным выводом). Если вы хотите run
выдавать исключение, когда процесс возвращает ненулевой код выхода, вы можете передать его check=True
. (Или вы можете проверить returncode
атрибут result
выше.) Когда безопасность не вызывает беспокойства, вы также можете запускать более сложные команды оболочки, передавая shell=True
как описано в конце этого ответа.
Более поздние версии Python еще больше упростили вышесказанное. В Python 3.7+ приведенная выше однострочная строка может быть написана следующим образом:
>>> subprocess.run(['ls', '-l'], capture_output=True, text=True).stdout
'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
Использование run
этого способа добавляет немного сложности по сравнению со старым способом выполнения задач. Но теперь вы можете делать практически все, что вам нужно, с помощью одной только run
функции.
Более старые версии Python (3-3.4): подробнее check_output
If you are using an older version of Python, or need modest backwards compatibility, you can use the check_output
function as briefly described above. It has been available since Python 2.7.
subprocess.check_output(*popenargs, **kwargs)
It takes takes the same arguments as Popen
(see below), and returns a string containing the program's output. The beginning of this answer has a more detailed usage example. In Python 3.5+, check_output
is equivalent to executing run
with check=True
and stdout=PIPE
, and returning just the stdout
attribute.
You can pass stderr=subprocess.STDOUT
to ensure that error messages are included in the returned output. When security is not a concern, you can also run more complex shell commands by passing shell=True
as described at the end of this answer.
Если вам нужно выполнить передачу данных из stderr
или передать входные данные процессу, check_output
это не будет соответствовать задаче. В этом случае смотрите Popen
Примеры ниже.
Сложные приложения и устаревшие версии Python (2.6 и ниже): Popen
Если вам нужна глубокая обратная совместимость или если вам нужна более сложная функциональность, чем предоставляет check_output
or run
, вам придется работать напрямую с Popen
объектами, которые инкапсулируют низкоуровневый API для подпроцессов.
Popen
Конструктор принимает либо единственную команду без аргументов, либо список, содержащий команду в качестве первого элемента, за которым следует любое количество аргументов, каждый как отдельный элемент в списке. shlex.split
может помочь преобразовать строки в списки соответствующего формата. Popen
объекты также принимают множество различных аргументов для управления процессом ввода-вывода и низкоуровневой конфигурации.
Для отправки входных данных и захвата выходных данных communicate
почти всегда является предпочтительным методом. Как в:
output = subprocess.Popen(["mycmd", "myarg"],
stdout=subprocess.PIPE).communicate()[0]
Или
>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE,
... stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo
Если вы установите stdin=PIPE
, communicate
также позволяет передавать данные процессу через 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
Обратите внимание на ответ Аарона Холла, который указывает, что в некоторых системах вам может потребоваться установить stdout
, stderr
и stdin
all в PIPE
(или DEVNULL
), чтобы заставить communicate
вообще работать.
В некоторых редких случаях вам может потребоваться сложный захват выходных данных в режиме реального времени. Ответ Vartec предлагает путь вперед, но методы, отличные от communicate
, склонны к взаимоблокировкам, если не использовать их осторожно.
Как и во всех вышеперечисленных функциях, когда безопасность не вызывает беспокойства, вы можете запускать более сложные команды оболочки, передавая shell=True
.
Примечания
1. Запуск команд оболочки: shell=True
аргумент
Обычно каждый вызов run
, check_output
или Popen
конструктора выполняет единственную программу,,,,,,,,,,,. Это означает отсутствие причудливых каналов в стиле bash. Если вы хотите запускать сложные команды оболочки, вы можете передать shell=True
, который поддерживают все три функции. Например:
>>> subprocess.check_output('cat books/* | wc', shell=True, text=True)
' 1299377 17005208 101299376\n'
Однако при выполнении этого возникают проблемы безопасности. Если вы делаете что-то большее, чем простые сценарии, возможно, вам было бы лучше вызывать каждый процесс отдельно и передавать выходные данные от каждого в качестве входных данных следующему, через
run(cmd, [stdout=etc...], input=other_output)
Или
Popen(cmd, [stdout=etc...]).communicate(other_output)
Соблазн напрямую подключить каналы силен; сопротивляйтесь ему. В противном случае вы, вероятно, столкнетесь с взаимоблокировками или вам придется делать хакерские вещи, подобные этому.
Ответ 2
Это намного проще, но работает только в Unix (включая Cygwin) и Python2.7.
import commands
print commands.getstatusoutput('wc -l file')
Он возвращает кортеж с (return_value, output).
Для решения, которое работает как в Python2, так и в Python3, используйте вместо этого subprocess
модуль:
from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response
Ответ 3
python3
Предложенияsubprocess.getoutput()
:
import subprocess
output = subprocess.getoutput("ls -l")
print(output)
Ответ 4
Что-то вроде этого:
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
Обратите внимание, что я перенаправляю stderr на stdout, возможно, это не совсем то, что вы хотите, но мне также нужны сообщения об ошибках.
Эта функция выдает строку за строкой по мере их поступления (обычно вам нужно дождаться завершения подпроцесса, чтобы получить выходные данные в целом).
В вашем случае использование будет:
for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
print line,