Запуск команды оболочки из Python и захват вывода
Я хочу написать функцию, которая будет выполнять команду shell и возвращает ее выход в строке, независимо от того, это сообщение об ошибке или успехе. Я просто хочу получить тот же результат, что и в командной строке.
что было бы примером кода, который сделал бы такую вещь?
например:
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'
13 ответов:
ответ на этот вопрос зависит от версии 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=Trueat конец этому ответу.The
check_outputфункция работает практически на всех версиях Python все еще широко используется (2.7+).2 но для более поздних версий, это больше не рекомендуемый подход.современные версии Python (3.5 или выше):
runесли вы используете Python 3.5 и не нужна обратная совместимость на новая
runфункции рекомендуется. Оно предоставляет очень общий, высокоуровневый API дляsubprocessмодуль. Чтобы захватить вывод программы, передайтеsubprocess.PIPEфлаг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() > 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как указано в примечаниях ниже.это добавляет только немного сложность, по сравнению со старым способом ведения дел. Но я думаю, что это стоит того: теперь вы можете сделать почти все, что вам нужно сделать с .
старые версии Python (2.7-3.4):
check_outputесли вы используете старую версию Python или вам нужна скромная обратная совместимость, вы, вероятно, можете использовать ) > 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все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. Юникод соображения
check_outputвозвращает строку в Python 2, но a
Это намного проще, но работает только на Unix (включая Cygwin).
import commands print commands.getstatusoutput('wc -l file')он возвращает кортеж с (return_value, output)
что-то вроде этого:
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,
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)
Это хитрый но супер просто решение, которое работает во многих ситуациях:
import os os.system('sample_cmd > tmp') print open('tmp', 'r').read()временный файл (здесь tmp) создается с выводом команды, и вы можете прочитать из него желаемый вывод.
дополнительная заметка из комментариев: Вы можете удалить файл tmp в случае разовые работы. Если вам нужно сделать это несколько раз, нет необходимости удалять tmp.
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()
ваш пробег может варьироваться, я попытался @ senderle's spin на решении 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]узнав больше, я считаю, что я нужны эти аргументы pipe, потому что я работаю над пользовательской системой, которая использует разные дескрипторы, поэтому мне пришлось напрямую управлять всеми 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')
у меня была такая же проблема Но придумал очень простой способ сделать это следуйте за этим
import subprocess Input = subprocess.getoutput("ls -l") print(Input)надеюсь, это поможет
Примечание: это решение python3 специфично как
subprocess.getoutput()не работает в python2
у меня был немного другой вкус той же проблемы со следующими требованиями:
- захват и возврат сообщений STDOUT по мере их накопления в буфере STDOUT (т. е. в реальном времени).
- @vartec решил эту Pythonically с его использованием генераторов и 'выход'
ключевое слово- вывести все строки STDOUT (даже если процесс завершается до буфера STDOUT может быть полностью читайте)
- Не тратьте циклы процессора на опрос процесса на высокой частоте
- проверьте код возврата подпроцесса
- печать STDERR (отдельно от STDOUT), если мы получаем ненулевой код возврата ошибки.
Я объединил и изменил предыдущие ответы, чтобы придумать следующее:
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)
Если вам нужно запустить команду оболочки для нескольких файлов, это сделало трюк для меня.
import os import subprocess # Define a function for running commands and capturing stdout line by line # (Modified from Vartec's 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редактировать: только что видел решение Макс Перссон с предложением Дж. Ф. Себастьян. Пошел вперед и включил это.
например, выполнить ('ls-ahl') дифференцированные три / четыре возможных возврата и платформы ОС:
- нет вывода, но запустить успешно
- выведите пустую строку, запустите успешно
- запустить не удалось
- выведите что-нибудь, запустите успешно
Comments