Выполнение команды оболочки и захват вывода

909

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

Каким был бы пример кода, который сделал бы такую ​​вещь?

Например:

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'
Серебряный свет
источник
2
связанные: stackoverflow.com/questions/2924310/…
jfs

Ответы:

1141

Ответ на этот вопрос зависит от версии 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 Но для более поздних версий это уже не рекомендуемый подход.

Современные версии 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']
>>> 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(capture to result.stderr) или stderr=subprocess.STDOUT(capture to result.stdoutвместе с обычным выводом). Когда безопасность не имеет значения, вы также можете запускать более сложные команды оболочки, передавая, shell=Trueкак описано в примечаниях ниже.

Это добавляет немного сложности по сравнению со старым способом ведения дел. Но я думаю, что это того стоит: теперь вы можете делать практически все, что вам нужно, с помощью runодной функции.

Старые версии Python (2.7-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к тому , чтобы сообщения об ошибках включены в возвращенном выходе - но в некоторых версиях Python перехода stderr=subprocess.PIPEк check_outputможет привести к тупикам . Когда безопасность не имеет значения, вы также можете запускать более сложные команды оболочки, передавая, shell=Trueкак описано в примечаниях ниже.

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

Сложные приложения и устаревшие версии Python (2.6 и ниже): 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

Примечание ответ Аарон Холла , который указывает , что на некоторых системах, вам может понадобиться набор stdout,stderr и stdinвсе PIPE(или DEVNULL) , чтобы получить , communicateчтобы работать на всех.

В некоторых редких случаях вам может потребоваться сложный захват выходных данных в режиме реального времени. Ответ Вартека предлагает дальнейший путь, но методы, отличные от communicateтупиковых, если их не использовать осторожно.

Как и во всех вышеперечисленных функциях, когда безопасность не имеет значения, вы можете передавать более сложные команды оболочки, передавая их shell=True.

Ноты

1. Запуск команд оболочки: shell=True аргумент

Как правило, каждый вызов run, check_outputили Popenконструктор выполняет одну программу . Это означает, что нет причудливых трубок в стиле Баш. Если вы хотите запускать сложные команды оболочки, вы можете передать shell=True, что поддерживают все три функции.

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

run(cmd, [stdout=etc...], input=other_output)

Или

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

Искушение напрямую соединить трубы сильно; сопротивляться этому. В противном случае, вы , вероятно , увидеть тупики или должны сделать Hacky таких вещей , как это .

2. Юникод соображения

check_outputвозвращает строку в Python 2, но bytesобъект в Python 3. Стоит потратить некоторое время на изучение юникода, если вы этого еще не сделали.

senderle
источник
5
И с, check_output()и communicate()вы должны подождать, пока процесс не будет завершен, когда poll()вы получите результат, как он есть. На самом деле зависит, что вам нужно.
vartec
2
Не уверен, относится ли это только к более поздним версиям Python, но переменная outбыла <class 'bytes'>для меня типа . Чтобы получить вывод в виде строки, мне пришлось декодировать его перед печатью так:out.decode("utf-8")
PolyMesh
1
@par Это не работает для вас, когда вы проходите shell=True? Меня устраивает. Вам не нужно, shlex.splitкогда вы проходите shell=True. shlex.splitдля команд без оболочки. Я думаю, что я собираюсь взять это немного, потому что это мутит воду.
senderle
2
Python 3.5+ допускает использование ключевого аргумента, universal_newlines=Trueкоторый позволяет передавать и выводить строки Unicode в кодировке системы по умолчанию. В 3.7 это было переименовано в более разумное text=True.
tripleee
2
Для Python 3.6+ вы можете использовать encodingпараметр subprocess.runвместо того, чтобы использовать result.stdout.decode('utf-8'), вы можете использовать subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8').
Пьер
191

Это намного проще, но работает только на 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
byte_array
источник
31
Устаревший, но очень полезный для старых версий Python без subprocess.check_output
static_rtti
22
Обратите внимание, что это зависит от Unix. Например, в Windows произойдет сбой.
Цитракс
4
+1 Я должен работать над древней версией Python 2.4, и это было ОЧЕНЬ полезно
javadba
1
Что такое PIPE чувак, давай покажи полный код: subprocess.PIPE
Kyle Bridenstine
@KyleBridenstine вы можете редактировать ответы.
Борис
106

Что-то такое:

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,
Vartec
источник
Не забудьте реализовать какой-либо активный цикл, чтобы получить выходные данные, чтобы избежать потенциальной тупиковой ситуации waitи callфункций.
Андре Карон
@Silver Light: ваш процесс, вероятно, ожидает ввода от пользователя. Попробуйте указать PIPEзначение stdinи закрыть этот файл, как только Popenвернетесь.
Андре Карон
4
-1: это бесконечный цикл, если retcodeесть 0. Проверка должна быть if retcode is not None. Вы не должны давать пустые строки (даже пустая строка, по крайней мере один символ «\ п»): if line: yield line. Позвоните p.stdout.close()в конце.
Jfs
2
Я пробовал код с помощью ls -l / dirname, и он ломается после перечисления двух файлов, в то время как в каталоге гораздо больше файлов
Vasilis
3
@fuenfundachtzig: .readlines()не вернется, пока не будет прочитан весь вывод, и поэтому он прерывается для большого вывода, который не помещается в памяти. Также, чтобы избежать пропущенных буферизованных данных после выхода из подпроцесса, должен быть аналогif retcode is not None: yield from p.stdout.readlines(); break
jfs
67

Ответ Вартека не читает все строки, поэтому я сделал версию, которая сделала:

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)
Макс Экман
источник
6
Вы можете использовать return iter(p.stdout.readline, b'')вместо цикла while
JFS
2
Это довольно крутое использование iter, не знал этого! Я обновил код.
Макс Экман
Я почти уверен, что stdout сохраняет весь вывод, это объект потока с буфером. Я использую очень похожую технику для истощения всех оставшихся выходных данных после завершения Popen, и в моем случае, используя poll () и readline во время выполнения для захвата выходных данных также.
Макс Экман
Я удалил свой вводящий в заблуждение комментарий. Я могу подтвердить, p.stdout.readline()может вернуть непустые ранее буферизованные выходные данные, даже если дочерний процесс уже завершился ( p.poll()не является None).
JFS
Этот код не работает. Смотрите здесь stackoverflow.com/questions/24340877/...
Тханг
61

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

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

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

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

os.remove('tmp')
Мехди Саман Буй
источник
5
Хаки, но супер просто + работает где угодно ... я могу объединить это с тем, mktempчтобы заставить его работать в многопоточных ситуациях, я думаю
Prakash Rajagaopal
2
Возможно, самый быстрый способ, но лучше добавить, os.remove('tmp')чтобы он был «без файлов».
XuMuK
@XuMuK Ты прав в случае разовой работы. Если это повторяющаяся работа, возможно, удаление не нужно
Мехди Саман
1
плохо для параллелизма, плохо для реентерабельных функций, плохо для того, чтобы не покидать систему, какой она была до ее запуска (без очистки)
2mia
1
@ 2mia Очевидно, что это легко по причине! Если вы хотите использовать файл в качестве некой разделяемой памяти для одновременного чтения и записи, это не лучший выбор. Но для кого-то например, вывод команды (например, ls или find или ...) может быть хорошим и быстрым выбором. Кстати, если вам нужно быстрое решение простой проблемы, я думаю, это лучшее. Если вам нужен конвейер, подпроцесс работает для вас более эффективно.
Мехди Саман Буй
44

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

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

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

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

azhar22k
источник
Как это решает проблему ОП? Пожалуйста, дополните.
RamenChef
4
Он возвращает вывод команды в виде строки, так же просто, как это
azhar22k
1
Конечно, print - это утверждение на Python 2. Вы должны быть в состоянии понять, что это ответ на Python 3.
2
@Dev print (s) является допустимым Python 2. subprocess.getoutput - нет.
user48956
2
В большинстве случаев это то, что люди, вероятно, захотят: легко запомнить, не нужно декодировать результаты и т. Д. Спасибо.
bwv549
18

Вы можете использовать следующие команды для запуска любой команды оболочки. Я использовал их на 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")
Мухаммед Хасан
источник
2
Устаревший с версии 2.6 - docs.python.org/2/library/os.html#os.popen
Филиппо Витале
1
@FilippoVitale Спасибо. Я не знал, что это устарело.
Мухаммед Хасан
1
Согласно raspberrypi.stackexchange.com/questions/71547/… os.popen() устарела в Python 2.6, но не рекомендуется в Python 3.x, поскольку в 3.x она реализована с использованием subprocess.Popen().
JL
12

Ваш пробег может меняться, я попытался @ 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')
Аарон Холл
источник
1
Интересно - это должно быть в Windows. Я добавлю примечание, указывающее на это на случай, если люди получат похожие ошибки.
senderle
использовать DEVNULLвместо из , subprocess.PIPEесли не писать / читать из трубы в противном случае вы можете повесить дочерний процесс.
JFS
10

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

  1. Захватывать и возвращать сообщения STDOUT по мере их накопления в буфере STDOUT (т.е. в режиме реального времени).
    • @vartec решила это с помощью генераторов и приведенного
      выше ключевого слова yield
  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)
Аэльфинн
источник
1
Не могли бы вы объяснить, как добавление sleep (.1) не приведет к потере циклов ЦП?
Моатаз Эльмасри
2
Если бы мы продолжали звонить p.poll()без перерыва между вызовами, мы бы тратили циклы ЦП, вызывая эту функцию миллионы раз. Вместо этого мы «дросселируем» наш цикл, сообщая ОС, что нам не нужно беспокоиться в течение следующей 1/10 секунды, чтобы она могла выполнять другие задачи. (Возможно, что p.poll () также спит, что делает наш оператор сна избыточным).
Aelfinn
5

Разделение начальной команды для команды 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...'
Артур Барсегян
источник
1
shlex.split()это удобно, особенно если вы не знаете, как именно работает цитирование в оболочке; но вручную преобразовать эту строку в список ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']совсем не сложно, если вы понимаете цитирование.
tripleee
4

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

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

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

Этан Страйдер
источник
Popenпринимает либо строку, но затем вам нужно shell=True, либо список аргументов, в этом случае вы должны передать ['nm', filename]вместо строки. Последнее предпочтительнее, потому что оболочка добавляет сложности, не предоставляя здесь никакой ценности. Передача строки без shell=Trueвидимых действий работает в Windows, но это может измениться в любой следующей версии Python.
tripleee
2

Согласно @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

Нео ли
источник
Вы заново изобретаете аргументы ключевого слова check=True, universal_newlines=True. Другими словами, subprocess.run()уже делает все, что делает ваш код.
трипл
1

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

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

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
milanbalazs
источник
Еще одно плохое переосмысление subprocess.run(). Не изобретай велосипед.
tripleee
0

например, 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 ""
Джерри Т
источник
0

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

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))
Г-Н
источник
Конечно, может, но почему вы хотите; и почему вы используете оболочку вместо передачи stdout=temp_file?
tripleee
На самом деле, в общем, вы правы, но в моем примере ecls.exeкажется, что используется другой инструмент командной строки, поэтому простой способ иногда не работает.
М.Р.
0

просто написал небольшой скрипт 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)"
harish2704
источник