Запуск команды оболочки из 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'
938   13  

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=True at конец этому ответу.

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.1):

 res = subprocess.check_output(lcmd, stderr=subprocess.STDOUT)

В 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

у меня был немного другой вкус той же проблемы со следующими требованиями:

  1. захват и возврат сообщений STDOUT по мере их накопления в буфере STDOUT (т. е. в реальном времени).
    • @vartec решил эту Pythonically с его использованием генераторов и 'выход'
      ключевое слово
  2. вывести все строки STDOUT (даже если процесс завершается до буфера STDOUT может быть полностью читайте)
  3. Не тратьте циклы процессора на опрос процесса на высокой частоте
  4. проверьте код возврата подпроцесса
  5. печать 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') дифференцированные три / четыре возможных возврата и платформы ОС:

  1. нет вывода, но запустить успешно
  2. выведите пустую строку, запустите успешно
  3. запустить не удалось
  4. выведите что-нибудь, запустите успешно

Comments

    Ничего не найдено.