Запуск команды оболочки и захват вывода

Я хочу написать функцию, которая будет выполнять команду оболочки и возвращать ее вывод в виде строки, независимо от того, является ли это сообщением об ошибке или успехом. Я просто хочу получить тот же результат, что и в командной строке.

Какой пример кода мог бы сделать такое?

Например:

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'

person Silver Light    schedule 21.01.2011    source источник
comment
связанные: stackoverflow.com/questions/2924310/   -  person jfs    schedule 24.01.2011


Ответы (19)


Во всех официально поддерживаемых версиях Python самым простым подходом является использование subprocess.check_output < / a> функция:

>>> 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

Если вы используете старую версию Python или вам нужна умеренная обратная совместимость, вы можете использовать функцию check_output, как кратко описано выше. Он доступен начиная с Python 2.7.

subprocess.check_output(*popenargs, **kwargs)  

Он принимает те же аргументы, что и Popen (см. Ниже), и возвращает строку, содержащую вывод программы. В начале этого ответа есть более подробный пример использования. В Python 3.5+ check_output эквивалентно выполнению run с check=True и stdout=PIPE и возврату только атрибута stdout.

Вы можете передать stderr=subprocess.STDOUT, чтобы гарантировать включение сообщений об ошибках в возвращаемый результат. Когда безопасность не важна, вы также можете запускать более сложные команды оболочки, передав shell=True, как описано в конце этого ответа.

Если вам нужно передать по конвейеру от stderr или передать ввод процессу, check_output не справится с этой задачей. В этом случае см. Popen примеры ниже.

Сложные приложения и устаревшие версии Python (2.6 и ниже): Popen

Если вам нужна глубокая обратная совместимость или если вам нужна более сложная функциональность, чем предоставляют check_output или run, вам придется работать напрямую с Popen объектами, которые инкапсулируют низкоуровневый API для подпроцессов.

Конструктор Popen принимает либо одну команду без аргументов, либо список, содержащий команду в качестве первого элемента, за которым следует любое количество аргументов, каждый как отдельный элемент в список. shlex.split может помочь преобразовать строки в списки с соответствующим форматом. Popen объекты также принимают множество различных аргументов для управления вводом-выводом процесса. и низкоуровневая конфигурация.

Для отправки ввода и вывода вывода почти всегда предпочтительным методом является communicate. Как в:

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

Or

>>> 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 все на 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)

Or

Popen(cmd, [stdout=etc...]).communicate(other_output)

Соблазн напрямую соединить трубы велик; сопротивляться этому. В противном случае вы, скорее всего, столкнетесь с тупиками или вам придется делать такие хакерские вещи, как this.

person senderle    schedule 21.01.2011
comment
И с check_output(), и с communicate() вам нужно дождаться завершения процесса, с poll() вы получите результат по мере его поступления. Действительно зависит от того, что вам нужно. - person vartec; 05.04.2012
comment
Не уверен, что это применимо только к более поздним версиям Python, но для меня переменная out имела тип <class 'bytes'>. Чтобы получить вывод в виде строки, мне пришлось декодировать ее перед печатью следующим образом: out.decode("utf-8") - person PolyMesh; 31.10.2013
comment
@PolyMesh: out равно bytes во всех версиях Python, кроме universal_newlines=True на Python 3. Выражение print ясно указывает, что это код Python 2, где bytes = str. - person jfs; 18.09.2014
comment
@senderle Вы сказали, что не передавайте stderr = subprocess.PIPE, и ваши примеры сделали именно это. Что ты имел в виду? Спасибо. - person Eleno; 14.06.2015
comment
//, есть ли способ сделать это с помощью STDOUT и STDERR вместо PIPE? - person Nathan Basanese; 31.08.2015
comment
Вы можете использовать параметр shell = True, а затем ввести команду со всеми параметрами cmd = ls -lh p = subprocess.Popen (cmd, shell = True, stdout = subprocess.PIPE) .communicate () Gives ('total 12K \ n-rw-r - r-- 1 dokwii dokwii 3.4K 19 апр. 16:57 routes.py \ ndrwxr-xr-x 5 dokwii dokwii 4.0K 15 апр. 16:41 static \ ndrwxr-xr-x 2 dokwii dokwii 4.0 K 18 апр, 10:23 шаблоны \ n ', Нет) - person David Okwii; 19.04.2016
comment
Как я могу это сделать, если у меня несколько подпроцессов? stackoverflow.com/questions/37683445/ - person zombi_man; 07.06.2016
comment
@senderle Это не работает, если я использую pipe и grep для захвата определенной части вывода. ps aux | grep /usr/lib/evolution/evolution-calendar-factory-subprocess | grep webcal | grep -oE org.gnome.evolution.dataserver.Subprocess.Backend.Calendarx[0-9][0-9]+x[0-9]+ Как правильно поступать с такими командами? - person Khurshid Alam; 27.03.2017
comment
Меня немного смущает то, как подпроцесс ожидает ввода. Почему мне нужно разбить мою команду linux на массив аргументов. Что, если я хочу, чтобы python запускал строку как команду в среде bash как черный ящик и просто ждал вывода и фиксировал его? Я просто не понимаю дополнительной сложности и хотел бы, чтобы мы по-прежнему могли использовать commands.getstatusoutput, которые теперь устарели - person Parsa; 10.04.2017

Это намного проще, но работает только в 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
person byte_array    schedule 13.02.2012
comment
Сейчас устарело, но очень полезно для старых версий python без subprocess.check_output - person static_rtti; 13.06.2012
comment
Обратите внимание, что это специфично для Unix. Например, это не сработает в Windows. - person Zitrax; 21.01.2013
comment
+1 Мне нужно работать над древней версией python 2.4, и это было ОЧЕНЬ полезно - person WestCoastProjects; 15.03.2014
comment
Что такое PIPE, чувак, покажи полный код: subprocess.PIPE - person Kyle Bridenstine; 30.10.2018

Что-то подобное:

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,
person vartec    schedule 21.01.2011
comment
Обязательно реализуйте какой-то активный цикл для получения вывода, чтобы избежать потенциальной тупиковой ситуации в функциях wait и call. - person André Caron; 21.01.2011
comment
@Silver Light: ваш процесс, вероятно, ожидает ввода от пользователя. Попробуйте указать PIPE значение для stdin и закрыть этот файл, как только Popen вернется. - person André Caron; 21.01.2011
comment
@Andre: да, на самом деле в моем производственном коде я использую threading.Timer () для выполнения p.terminate () по истечении времени ожидания. - person vartec; 21.01.2011
comment
@senderle: хорошо, но в моем случае я должен был быть совместим с 2.5 - person vartec; 21.01.2011
comment
-1: это бесконечный цикл, если retcode равно 0. Проверка должна быть if retcode is not None. Вы не должны давать пустые строки (даже в пустой строке есть хотя бы один символ '\ n'): if line: yield line. В конце звоните p.stdout.close(). - person jfs; 24.01.2011
comment
@ Себастьян: хорошая точка зрения на реткод; В моем конкретном случае это не имеет значения, потому что retcode всегда 255, но в общем случае вы абсолютно правы. - person vartec; 24.01.2011
comment
Это не сработает, если сразу возвращается более одной строки, вместо этого лучше использовать readlines() (обратите внимание на s). - person fuenfundachtzig; 21.09.2012
comment
кстати, вы можете просто вернуть iter(p.stdout.readline, b'') из runProcess () вместо цикла while. - person jfs; 22.11.2012
comment
Я пробовал код с ls -l / dirname, и он ломается после перечисления двух файлов, в то время как в каталоге намного больше файлов - person Vasilis; 01.10.2013
comment
@fuenfundachtzig: .readlines() не вернется, пока не будет прочитан весь вывод, и поэтому он прерывается для вывода большого объема, который не помещается в памяти. Также, чтобы не пропустить данные в буфере после выхода из подпроцесса, должен быть аналог if retcode is not None: yield from p.stdout.readlines(); break - person jfs; 21.12.2013

У меня была такая же проблема, но я понял очень простой способ сделать это:

import subprocess
output = subprocess.getoutput("ls -l")
print(output)

Надеюсь, это поможет

Примечание. Это решение специфично для Python3, поскольку subprocess.getoutput() не работает в Python2.

person azhar22k    schedule 12.11.2016
comment
Как это решает проблему OP? Пожалуйста, дополните. - person RamenChef; 12.11.2016
comment
Он возвращает вывод команды в виде строки, такой простой, как эта - person azhar22k; 04.12.2016
comment
Конечно, print - это оператор Python 2. Вы должны понять, что это ответ Python 3. - person ; 26.01.2017
comment
@Dev print (s) является допустимым python 2. subprocess.getoutput - нет. - person user48956; 27.04.2017
comment
В большинстве случаев это именно то, что люди могут захотеть: легко запомнить, не нужно декодировать результаты и т. Д. Спасибо. - person bwv549; 04.10.2019
comment
Это наиболее действенный ответ. Отлично работает с split () - person Poopy McFartnoise; 09.10.2019
comment
Лучшим ответом должен быть этот! - person mnsr; 25.11.2019
comment
Обратите внимание, что это явно помечено как устаревшая функция с плохой поддержкой обработки исключений и без гарантий безопасности. - person senderle; 30.01.2020
comment
Это лучший и точный ответ. - person Ryko; 19.06.2021

Ответ 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)
person Max Ekman    schedule 30.10.2012
comment
вы можете использовать return iter(p.stdout.readline, b'') вместо цикла while - person jfs; 22.11.2012
comment
Это довольно крутое использование iter, я этого не знал! Я обновил код. - person Max Ekman; 29.11.2012
comment
Я почти уверен, что stdout сохраняет весь вывод, это объект потока с буфером. Я использую очень похожую технику, чтобы исчерпать весь оставшийся вывод после завершения Popen, и в моем случае использую poll () и readline во время выполнения для захвата вывода в реальном времени. - person Max Ekman; 29.11.2012
comment
Я удалил свой вводящий в заблуждение комментарий. Я могу подтвердить, p.stdout.readline() может вернуть непустой ранее буферизованный вывод, даже если дочерний процесс уже завершился (p.poll() не None). - person jfs; 18.09.2014
comment
Этот код не работает. См. Здесь stackoverflow.com/questions/24340877/ - person thang; 03.05.2015
comment
@thang: shell=False по умолчанию, поэтому вы должны передавать команду в виде списка (а не в виде строки). Я обновил ответ, добавив отсутствующий .split(). - person jfs; 23.05.2015
comment
Как получить код выхода (возвращаемый echo $?) Выполняемой команды? - person Vespene Gas; 19.10.2018

Это сложное, но сверхпростое решение, которое работает во многих ситуациях:

import os
os.system('sample_cmd > tmp')
print(open('tmp', 'r').read())

Временный файл (здесь tmp) создается с выводом команды, и вы можете прочитать из него желаемый результат.

Дополнительное примечание из комментариев: вы можете удалить файл tmp в случае одноразовой работы. Если вам нужно сделать это несколько раз, нет необходимости удалять tmp.

os.remove('tmp')
person Mehdi Saman Booy    schedule 21.08.2016
comment
Хакерский, но супер простой + работает где угодно .. можно комбинировать его с mktemp, чтобы заставить его работать в многопоточных ситуациях, я думаю - person Prakash Rajagaopal; 18.10.2016
comment
Может быть, самый быстрый способ, но лучше добавить os.remove('tmp'), чтобы он не содержал файлов. - person XuMuK; 03.07.2017
comment
@XuMuK Вы правы в случае разовой работы. Если это повторяющаяся работа, возможно, в удалении нет необходимости. - person Mehdi Saman Booy; 05.07.2017
comment
плохо для параллелизма, плохо для реентерабельных функций, плохо для того, чтобы не оставить систему такой, какой она была до ее запуска (без очистки) - person 2mia; 13.07.2018
comment
@ 2mia Очевидно, это легко по какой-то причине! Если вы хотите использовать файл как своего рода разделяемую память для одновременных операций чтения и записи, это не лучший выбор. Но для s.th. например, вывод команды (например, ls или find или ...), это может быть хорошим и быстрым выбором. Кстати. Если вам нужно быстрое решение простой проблемы, я думаю, это лучшее. Если вам нужен конвейер, подпроцесс работает для вас более эффективно. - person Mehdi Saman Booy; 15.07.2018
comment
Я именно это и искал! - person Shahriar Rahman Zahin; 07.05.2020
comment
Я столкнулся с контекстами с несколькими уровнями вложенных подпроцессов, где я не мог напрямую опросить эти потоки или наследовать файловый дескриптор и т. Д. Принцип, проиллюстрированный здесь, решил проблему. Обратите внимание, что в некоторых случаях может быть проще сгенерировать файл оболочки / пакетного файла для сложной команды, выполнить его с перенаправлением потока снаружи, а затем удалить сценарий. - person BuvinJ; 23.02.2021
comment
В зависимости от того, что вы делаете, обратите внимание, что вместо использования временного файла для вывода вы можете запустить регистратор в родительском скрипте, но при использовании этого метода закройте этот регистратор / файл, а затем используйте >> (добавить) в вашей системе. вызов этого файла. Затем перезапустите регистратор (также в режиме добавления) после завершения подпроцесса. - person BuvinJ; 23.02.2021

Вы можете использовать следующие команды для запуска любой команды оболочки. Я использовал их на Ubuntu.

import os
os.popen('your command here').read()

Примечание. Это устарело, начиная с python 2.6. Теперь вы должны использовать subprocess.Popen. Ниже приведен пример

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")
person Muhammad Hassan    schedule 04.04.2017
comment
Не рекомендуется, начиная с версии 2.6 - docs.python.org/2/library/os .html # os.popen - person Filippo Vitale; 26.05.2017
comment
@FilippoVitale Спасибо. Я не знал, что это устарело. - person Muhammad Hassan; 26.05.2017
comment
Согласно raspberrypi.stackexchange.com/questions/71547/ os.popen() устарело в Python 2.6, но не устарело в Python 3.x, поскольку в 3.x оно реализовано используя subprocess.Popen(). - person J-L; 13.08.2018

Ваш пробег может отличаться, я попытался использовать @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')
person Aaron Hall    schedule 18.02.2014
comment
Интересно - это должно быть вещь Windows. Я добавлю примечание, указывающее на это, на случай, если люди будут получать похожие ошибки. - person senderle; 01.05.2014
comment
используйте DEVNULL вместо вместо subprocess.PIPE, если вы не пишете / не читаете из канала, иначе вы можете повесить дочерний процесс. - person jfs; 09.09.2014

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

  1. Capture and return STDOUT messages as they accumulate in the STDOUT buffer (i.e. in realtime).
    • @vartec solved this Pythonically with his use of generators and the 'yield'
      keyword above
  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)
person The Aelfinn    schedule 19.10.2016
comment
Не могли бы вы объяснить, как добавление режима сна (.1) не тратит циклы процессора? - person Moataz Elmasry; 02.08.2017
comment
Если бы мы продолжали вызывать p.poll() без сна между вызовами, мы потратили бы циклы ЦП, вызывая эту функцию миллионы раз. Вместо этого мы ограничиваем наш цикл, сообщая ОС, что нам не нужно беспокоиться в течение следующей 1/10 секунды, чтобы она могла выполнять другие задачи. (Возможно, что p.poll () тоже спит, что делает наш оператор сна излишним). - person The Aelfinn; 02.08.2017

Разделение начальной команды для subprocess может быть непростым и громоздким.

Используйте shlex.split(), чтобы помочь себе.

Пример команды

git log -n 5 --since "5 years ago" --until "2 year ago"

Код

from subprocess import check_output
from shlex import split

res = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

Без shlex.split() код выглядел бы следующим образом

res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    '5 years ago', 
    '--until', 
    '2 year ago'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'
person Artur Barseghyan    schedule 15.12.2018
comment
shlex.split() - это удобство, особенно если вы не знаете, как именно работает кавычка в оболочке; но вручную преобразовать эту строку в список ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago'] совсем несложно, если вы понимаете цитирование. - person tripleee; 18.06.2019

Вот решение, работающее, если вы хотите распечатать вывод во время работы процесса или нет.


Я также добавил текущий рабочий каталог, он мне пригодился не раз.


Надеясь, что решение кому-то поможет :).

import subprocess

def run_command(cmd_and_args, print_constantly=False, cwd=None):
"""Runs a system command.

:param cmd_and_args: the command to run with or without a Pipe (|).
:param print_constantly: If True then the output is logged in continuous until the command ended.
:param cwd: the current working directory (the directory from which you will like to execute the command)
:return: - a tuple containing the return code, the stdout and the stderr of the command
"""
output = []

process = subprocess.Popen(cmd_and_args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)

while True:
    next_line = process.stdout.readline()
    if next_line:
        output.append(str(next_line))
        if print_constantly:
            print(next_line)
    elif not process.poll():
        break

error = process.communicate()[1]

return process.returncode, '\n'.join(output), error
person Joy Jedidja Ndjama    schedule 29.07.2020
comment
Работает как на Python 3, так и на 2.7 - person Joy Jedidja Ndjama; 30.07.2020

Если вам нужно запустить команду оболочки для нескольких файлов, это помогло мне.

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

Изменить: только что увидел решение Макса Перссона с предложением Дж. Ф. Себастьяна. Пошел вперед и включил это.

person Ethan Strider    schedule 01.04.2015
comment
Popen принимает либо строку, но тогда вам понадобится shell=True, либо список аргументов, и в этом случае вы должны передать ['nm', filename] вместо строки. Последнее предпочтительнее, потому что оболочка добавляет сложности, не представляя здесь никакой ценности. Передача строки без shell=True, по-видимому, работает в Windows, но это может измениться в любой следующей версии Python. - person tripleee; 18.06.2019

Согласно @senderle, если вы используете python3.6, как я:

def sh(cmd, input=""):
    rst = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))
    assert rst.returncode == 0, rst.stderr.decode("utf-8")
    return rst.stdout.decode("utf-8")
sh("ls -a")

Будет действовать точно так же, как вы запускаете команду в bash

person Neo li    schedule 20.05.2019
comment
Вы заново изобретаете ключевое слово arguments check=True, universal_newlines=True. Другими словами, subprocess.run() уже делает все, что делает ваш код. - person tripleee; 12.06.2019

Если вы используете модуль subprocess python, вы можете обрабатывать STDOUT, STDERR и код возврата команды отдельно. Вы можете увидеть пример полной реализации вызывающей команды. Конечно, вы можете расширить его с помощью try..except, если хотите.

Приведенная ниже функция возвращает STDOUT, STDERR и код возврата, чтобы вы могли обработать их в другом скрипте.

import subprocess

def command_caller(command=None)
    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False)
    out, err = sp.communicate()
    if sp.returncode:
        print(
            "Return code: %(ret_code)s Error message: %(err_msg)s"
            % {"ret_code": sp.returncode, "err_msg": err}
            )
    return sp.returncode, out, err
person milanbalazs    schedule 20.05.2019
comment
Еще одна неудачная реализация subprocess.run(). Не изобретайте велосипед. - person tripleee; 12.06.2019

Я хотел бы предложить simppl в качестве варианта для рассмотрения. Это модуль, доступный через pypi: pip install simppl и работающий на python3.

simppl позволяет пользователю запускать команды оболочки и читать вывод с экрана.

Разработчики предлагают три варианта использования:

  1. Самое простое использование будет выглядеть так:
    from simppl.simple_pipeline import SimplePipeline
    sp = SimplePipeline(start=0, end=100):
    sp.print_and_run('<YOUR_FIRST_OS_COMMAND>')
    sp.print_and_run('<YOUR_SECOND_OS_COMMAND>') ```

  1. Для одновременного запуска нескольких команд используйте:
    commands = ['<YOUR_FIRST_OS_COMMAND>', '<YOUR_SECOND_OS_COMMAND>']
    max_number_of_processes = 4
    sp.run_parallel(commands, max_number_of_processes) ```

  1. Наконец, если ваш проект использует модуль cli, вы можете напрямую запустить другой command_line_tool как часть конвейера. Другой инструмент будет запущен из того же процесса, но он будет отображаться в журналах как другая команда в конвейере. Это обеспечивает более плавную отладку и рефакторинг инструментов, вызывающих другие инструменты.
    from example_module import example_tool
    sp.print_and_run_clt(example_tool.run, ['first_number', 'second_nmber'], 
                                 {'-key1': 'val1', '-key2': 'val2'},
                                 {'--flag'}) ```

Обратите внимание, что печать в STDOUT / STDERR осуществляется через модуль logging python.


Вот полный код, показывающий, как работает simppl:

import logging
from logging.config import dictConfig

logging_config = dict(
    version = 1,
    formatters = {
        'f': {'format':
              '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
        },
    handlers = {
        'h': {'class': 'logging.StreamHandler',
              'formatter': 'f',
              'level': logging.DEBUG}
        },
    root = {
        'handlers': ['h'],
        'level': logging.DEBUG,
        },
)
dictConfig(logging_config)

from simppl.simple_pipeline import SimplePipeline
sp = SimplePipeline(0, 100)
sp.print_and_run('ls')
person 0x90    schedule 16.10.2020

В Python 3.7+ используйте _1 _ и передайте capture_output=True:

import subprocess
result = subprocess.run(['echo', 'hello', 'world'], capture_output=True)
print(repr(result.stdout))

Это вернет байты:

b'hello world\n'

Если вы хотите преобразовать байты в строку, добавьте text=True:

result = subprocess.run(['echo', 'hello', 'world'], capture_output=True, text=True)
print(repr(result.stdout))

Это будет читать байты с использованием кодировки по умолчанию:

'hello world\n'

Если вам нужно вручную указать другую кодировку, используйте encoding="your encoding" вместо text=True:

result = subprocess.run(['echo', 'hello', 'world'], capture_output=True, encoding="utf8")
print(repr(result.stdout))
person Boris    schedule 17.05.2021

например, execute ('ls -ahl') различает три / четыре возможных возврата и платформы ОС:

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

функция ниже

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 ""
person Jerry T    schedule 02.08.2017

Вывод можно перенаправить в текстовый файл, а затем прочитать его.

import subprocess
import os
import tempfile

def execute_to_file(command):
    """
    This function execute the command
    and pass its output to a tempfile then read it back
    It is usefull for process that deploy child process
    """
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    path = temp_file.name
    command = command + " > " + path
    proc = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if proc.stderr:
        # if command failed return
        os.unlink(path)
        return
    with open(path, 'r') as f:
        data = f.read()
    os.unlink(path)
    return data

if __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))
person Masoud Rahimi    schedule 22.05.2019
comment
Конечно, может, но зачем вам это нужно; и зачем использовать оболочку вместо передачи stdout=temp_file? - person tripleee; 18.06.2019
comment
На самом деле, в целом вы правы, но в моем примере ecls.exe, похоже, развертывает другой инструмент командной строки, поэтому простой способ иногда не работал. - person Masoud Rahimi; 18.06.2019

просто написал небольшой сценарий bash, чтобы сделать это с помощью curl

https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5

#!/usr/bin/env bash

# Usage: gdrive_dl.sh <url>

urlBase='https://drive.google.com'
fCookie=tmpcookies

curl="curl -L -b $fCookie -c $fCookie"
confirm(){
    $curl "$1" | grep jfk-button-action | sed -e 's/.*jfk-button-action" href="\(\S*\)".*/\1/' -e 's/\&amp;/\&/g'
}

$curl -O -J "${urlBase}$(confirm $1)"
person harish2704    schedule 13.04.2020