Я хочу написать функцию, которая выполнит команду оболочки и вернет ее вывод в виде строки, независимо от того, является ли это сообщение об ошибке или успехе. Я просто хочу получить тот же результат, что и в командной строке.
Каким будет пример кода, который бы сделал такую вещь?
Например:
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'
Ответ на этот вопрос зависит от версии используемого вами 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, которые все еще широко используются (2.7 +). 2 Но для более поздних версий это уже не рекомендуемый подход.
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']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'
Вы можете захватить ошибки, передав stderr=subprocess.PIPE
(захват в result.stderr
) или stderr=subprocess.STDOUT
(захват до result.stdout
вместе с регулярным выходом). Когда безопасность не вызывает беспокойства, вы также можете запускать более сложные команды оболочки, передавая shell=True
, как описано в примечаниях ниже.
Это добавляет немного сложности, по сравнению со старым способом делать вещи. Но я думаю, что это стоит выигрыша: теперь вы можете делать почти все, что вам нужно, только с функцией run
.
check_output
Если вы используете более старую версию Python или вам нужна скромная обратная совместимость, вы, вероятно, можете использовать функцию check_output
, как описано выше. Он доступен с Python 2.7.
subprocess.check_output(*popenargs, **kwargs)
Он принимает те же аргументы, что и Popen
(см. ниже), и возвращает строку, содержащую вывод программы. В начале этого ответа приведен более подробный пример использования.
Вы можете передать stderr=subprocess.STDOUT
, чтобы сообщения об ошибках включались в возвращаемый вывод, но не передавайте stderr=subprocess.PIPE
в check_output
. Это может вызвать взаимоблокировки. Когда безопасность не вызывает беспокойства, вы также можете запускать более сложные команды оболочки, передавая shell=True
, как описано в примечаниях ниже.
Если вам нужно перейти от stderr
или передать ввод в процесс, check_output
не будет выполняться. Ниже приведены примеры Popen
.
Popen
Если вам нужна глубокая совместимость в обратном направлении или вам нужны более сложные функции, чем check_output
, вам придется работать непосредственно с объектами 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
Примечание ответ Aaron Hall, который указывает, что для некоторых систем вам может потребоваться установить stdout
, stderr
и stdin
все на PIPE
(или DEVNULL
), чтобы получить communicate
для работы вообще.
В некоторых редких случаях вам может потребоваться сложный захват в режиме реального времени. Vartec предлагает путь вперед, но методы, отличные от communicate
, склонны к взаимоблокировкам, если их не использовать осторожно.
Как и во всех вышеперечисленных функциях, когда безопасность не вызывает беспокойства, вы можете запускать более сложные команды оболочки, передавая shell=True
.
1. Выполнение команд оболочки: аргумент shell=True
Обычно каждый вызов run
, check_output
или конструктор Popen
выполняет одну программу. Это означает отсутствие фантастических bash -типов. Если вы хотите запускать сложные команды оболочки, вы можете передать shell=True
, который поддерживает все три функции.
Однако при этом возникает проблема безопасности. Если вы делаете что-то большее, чем легкие сценарии, вам может быть лучше вызывать каждый процесс по отдельности и передавать выходные данные из каждого в качестве входа в следующий, через
run(cmd, [stdout=etc...], input=other_output)
или
Popen(cmd, [stdout=etc...]).communicate(other_output)
Искушение напрямую подключать трубы является сильным; сопротивляться. В противном случае вы, скорее всего, увидите взаимоблокировки или должны сделать хакерские вещи, такие как .
2. Unicode соображения
check_output
возвращает строку в Python 2, но объект bytes
в Python 3. Необходимо потратить немного времени на узнать о unicode если вы еще этого не сделали.
check_output()
и communicate()
вы должны ждать , пока процесс не будет сделан, с poll()
вы получаете выход , как это происходит. На самом деле зависит, что вам нужно.
Это проще, но работает только с Unix (включая Cygwin).
import commands
print commands.getstatusoutput('wc -l file')
он возвращает кортеж с (return_value, output)
Это работает только в python2.7
: он недоступен на python3
. Для решения, которое работает в обоих, вместо этого используйте модуль subprocess
:
import subprocess
output=subprocess.Popen(["date"],stdout=PIPE)
response=output.communicate()
print response
Что-то вроде этого:
def runProcess(exe):
p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while(True):
retcode = p.poll() #returns None while subprocess is running
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,
wait
и call
.
Ответ Vartec не читает все строки, поэтому я сделал версию, которая сделала:
def run_command(command):
p = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
return iter(p.stdout.readline, b'')
Использование совпадает с принятым ответом:
command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
print(line)
return iter(p.stdout.readline, b'')
вместо цикла while
Это сложное, но супер простое решение, которое работает во многих ситуациях:
import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()
Временный файл (здесь tmp) создается с выходом команды, и вы можете прочитать из него ваш желаемый результат.
Дополнительная заметка из комментариев: Вы можете удалить файл tmp в случае одноразового задания. Если вам нужно сделать это несколько раз, нет необходимости удалять tmp.
os.remove('tmp')
mktemp
чтобы заставить его работать в многопоточных ситуациях
os.remove('tmp')
чтобы сделать его "безфайловым".
В Python 3.5:
import subprocess
output = subprocess.run("ls -l", shell=True, stdout=subprocess.PIPE,
universal_newlines=True)
print(output.stdout)
Вы можете использовать следующие команды для запуска любой команды оболочки. Я использовал их на ubuntu.
import os
os.popen('your command here').read()
У меня была такая же проблема. Но выяснилось, что это очень простой способ сделать это.
import subprocess
output = subprocess.getoutput("ls -l")
print(output)
Надеюсь, что это поможет
Примечание. Это решение является специфичным для python3, поскольку subprocess.getoutput()
не работает в python2
Современное решение Python ( >= 3.1):
res = subprocess.check_output(lcmd, stderr=subprocess.STDOUT)
check_output()
доступен начиная с Python 2.7.
Ваш пробег мая может, я попытался @senderle запустить решение Vartec в Windows на Python 2.6.5, но я получал ошибки, и никаких других решений не работало. Моя ошибка: WindowsError: [Error 6] The handle is invalid
.
Я обнаружил, что мне пришлось назначить PIPE каждому дескриптору, чтобы вернуть его, который я ожидал, - следующее работало для меня.
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()
и вызывайте так: ([0]
получает первый элемент кортежа, stdout
):
run_command('tracert 11.1.0.1')[0]
Узнав больше, я считаю, что мне нужны эти аргументы, потому что я работаю над настраиваемой системой, которая использует разные дескрипторы, поэтому мне пришлось напрямую управлять всеми файлами std.
Чтобы остановить всплывающие окна консоли (с помощью Windows), сделайте следующее:
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')
DEVNULL
вместо subprocess.PIPE
если вы не пишете / читаете из канала, иначе вы можете повесить дочерний процесс.
У меня был немного другой вкус той же проблемы со следующими требованиями:
Я объединил и изменил предыдущие ответы, чтобы придумать следующее:
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))
Этот код будет выполнен так же, как и предыдущие ответы:
for line in run_command(cmd):
print(line)
p.poll()
без перерыва между вызовами, мы бы тратили циклы ЦП, вызывая эту функцию миллионы раз. Вместо этого мы «дросселируем» наш цикл, сообщая ОС, что нам не нужно беспокоиться в течение следующей 1/10 секунды, чтобы она могла выполнять другие задачи. (Возможно, что p.poll () тоже спит, что делает наш оператор сна избыточным).
Если вам нужно запустить команду оболочки для нескольких файлов, это помогло мне.
import os
import subprocess
# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec 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
Изменить: просто увидел решение Макс Перссон с предложением Ю. Ф. Себастьяна. Поехал вперед и включил это.
'#!/usr/bin/python3 import subprocess nginx_ver = subprocess.getstatusoutput("nginx -v") print (nginx_ver)'
например, execute ('ls -ahl') дифференцированных трех/четырех возможных возвратов и платформ ОС:
ниже
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 ""