Executando o Comando Shell e Capturando a Saída

908

Quero escrever uma função que execute um comando shell e retorne sua saída como uma string , não importa se é uma mensagem de erro ou sucesso. Eu só quero obter o mesmo resultado que eu teria obtido com a linha de comando.

O que seria um exemplo de código que faria uma coisa dessas?

Por exemplo:

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'
Luz cinza
fonte

Respostas:

1139

A resposta a esta pergunta depende da versão do Python que você está usando. A abordagem mais simples é usar a subprocess.check_outputfunção:

>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

check_outputexecuta um único programa que recebe apenas argumentos como entrada. 1 Retorna o resultado exatamente como impresso stdout. Se você precisar escrever uma entrada stdin, pule para as seções runou Popen. Se você deseja executar comandos shell complexos, consulte a nota shell=Trueno final desta resposta.

A check_outputfunção funciona em quase todas as versões do Python ainda em uso amplo (2.7+). 2 Mas para versões mais recentes, não é mais a abordagem recomendada.

Versões modernas do Python (3.5 ou superior): run

Se você estiver usando o Python 3.5 ou superior e não precisar de compatibilidade com versões anteriores , a nova runfunção é recomendada. Ele fornece uma API de alto nível muito geral para o subprocessmódulo. Para capturar a saída de um programa, passe o subprocess.PIPEsinalizador para o stdoutargumento de palavra - chave. Em seguida, acesse o stdoutatributo do CompletedProcessobjeto retornado :

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

O valor de retorno é um bytesobjeto; portanto, se você deseja uma sequência adequada, precisará decodedisso. Supondo que o processo chamado retorne uma sequência codificada em UTF-8:

>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Tudo isso pode ser compactado em uma linha:

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

Se você deseja passar a entrada para o processo stdin, passe um bytesobjeto para o inputargumento de palavra - chave:

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

Você pode capturar erros passando stderr=subprocess.PIPE(capturar para result.stderr) ou stderr=subprocess.STDOUT(capturar para result.stdoutjunto com a saída regular). Quando a segurança não é uma preocupação, você também pode executar comandos shell mais complexos, passando shell=Trueconforme descrito nas notas abaixo.

Isso adiciona um pouco de complexidade, comparado à maneira antiga de fazer as coisas. Mas acho que vale a pena: agora você pode fazer quase tudo o que precisa fazer apenas com a runfunção.

Versões anteriores do Python (2.7-3.4): check_output

Se você estiver usando uma versão mais antiga do Python, ou precisar de uma modesta compatibilidade com versões anteriores, provavelmente poderá usar a check_outputfunção conforme descrito brevemente acima. Está disponível desde o Python 2.7.

subprocess.check_output(*popenargs, **kwargs)  

Ele usa os mesmos argumentos que Popen(veja abaixo) e retorna uma string contendo a saída do programa. O início desta resposta tem um exemplo de uso mais detalhado. No Python 3.5 e superior, check_outputé equivalente a executar runcom check=Truee stdout=PIPE, e retornar apenas o stdoutatributo.

Você pode transmitir stderr=subprocess.STDOUTpara garantir que as mensagens de erro sejam incluídas na saída retornada - mas, em algumas versões do Python, a passagem stderr=subprocess.PIPEpara check_outputpode causar conflitos . Quando a segurança não é uma preocupação, você também pode executar comandos shell mais complexos, passando shell=Trueconforme descrito nas notas abaixo.

Se você precisar canalizar stderrou passar a entrada para o processo, check_outputnão estará à altura da tarefa. Veja os Popenexemplos abaixo nesse caso.

Aplicativos complexos e versões herdadas do Python (2.6 e abaixo): Popen

Se você precisar de profunda compatibilidade com versões anteriores ou se precisar de funcionalidades mais sofisticadas do que as check_outputfornecidas, precisará trabalhar diretamente com Popenobjetos, que encapsulam a API de baixo nível para subprocessos.

O Popenconstrutor aceita um único comando sem argumentos ou uma lista contendo um comando como seu primeiro item, seguido por qualquer número de argumentos, cada um como um item separado na lista. shlex.splitpode ajudar a analisar seqüências de caracteres em listas formatadas adequadamente. Popenos objetos também aceitam vários argumentos diferentes para gerenciamento de E / S de processo e configuração de baixo nível.

Enviar entrada e capturar saída communicateé quase sempre o método preferido. Como em:

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

Ou

>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo

Se você definir stdin=PIPE, communicatetambém permitirá que você transmita dados para o processo via 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

Nota resposta de Aaron Hall , o que indica que em alguns sistemas, você pode precisar de conjunto stdout, stderre stdintodos PIPE(ou DEVNULL) para chegar communicateao trabalho em tudo.

Em alguns casos raros, você pode precisar de captura de saída complexa em tempo real. A resposta da Vartec sugere um caminho a seguir, mas outros métodos que não communicatesejam propensos a conflitos se não forem usados ​​com cuidado.

Como em todas as funções acima, quando a segurança não é uma preocupação, você pode executar comandos shell mais complexos passando shell=True.

Notas

1. Executando comandos do shell: o shell=Trueargumento

Normalmente, cada chamada para run, check_outputou o Popenconstrutor executa um único programa . Isso significa que não há tubos sofisticados no estilo bash. Se você deseja executar comandos complexos do shell, pode passar shell=True, que todas as três funções suportam.

No entanto, isso gera preocupações de segurança . Se estiver fazendo algo além de scripts leves, convém chamar cada processo separadamente e passar a saída de cada um como entrada para a próxima, via

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

Ou

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

A tentação de conectar tubos diretamente é forte; resista. Caso contrário, você provavelmente verá impasses ou precisará fazer coisas hacky como essa .

2. Considerações Unicode

check_outputretorna uma string no Python 2, mas um bytesobjeto no Python 3. Vale a pena dedicar um momento para aprender sobre o unicode, se você ainda não o fez.

remetente
fonte
5
Ambos com check_output()e communicate()você tem que esperar até que o processo é feito, com poll()que você está recebendo a saída como ela vem. Realmente depende do que você precisa.
vartec
2
Não tenho certeza se isso se aplica apenas a versões posteriores do Python, mas a variável outera do tipo <class 'bytes'>para mim. A fim de obter o resultado como uma string que eu tinha para decodificá-lo antes de imprimir assim:out.decode("utf-8")
polymesh
1
@par Isso não funciona para você quando você passa shell=True? Funciona para mim. Você não precisa shlex.splitquando passa shell=True. shlex.splité para comandos não-shell. Acho que vou tirar isso porque isso está turvando as águas.
Senderle
2
O Python 3.5+ permite um argumento de palavra-chave universal_newlines=Trueque permite transmitir e extrair cadeias Unicode na codificação padrão do sistema. Em 3.7, isso foi renomeado para o mais sensato text=True.
tripleee
2
Para Python 3.6+, você pode usar o encodingparâmetro, em subprocess.runvez de usar result.stdout.decode('utf-8'), você pode usar subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8').
224 Pierre
191

Isso é bem mais fácil, mas funciona apenas no Unix (incluindo Cygwin) e Python2.7.

import commands
print commands.getstatusoutput('wc -l file')

Retorna uma tupla com o (return_value, output).

Para uma solução que funcione no Python2 e Python3, use o subprocessmódulo:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response
byte_array
fonte
31
Obsoleta agora, mas muito útil para versões antigas Python sem subprocess.check_output
static_rtti
22
Observe que isso é específico do Unix. Por exemplo, falhará no Windows.
Zitrax
4
+1 eu tenho que trabalhar na versão antiga de Python 2.4 e isso foi muito útil
javadba
1
O que é TUBO cara vamos mostrar o código completo: subprocess.PIPE
Kyle Bridenstine
@KyleBridenstine você pode editar respostas.
Boris
106

Algo parecido:

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

Observe que estou redirecionando o stderr para o stdout, pode não ser exatamente o que você deseja, mas também quero mensagens de erro.

Essa função gera linha por linha conforme elas chegam (normalmente você teria que esperar o subprocesso terminar para obter a saída como um todo).

Para o seu caso, o uso seria:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,
vartec
fonte
Certifique-se de implementar algum tipo de loop ativo para obter a saída para evitar o possível impasse waite callfunções.
André Caron
@ Silver Silver: seu processo provavelmente está aguardando a entrada do usuário. Tente fornecer um PIPEvalor stdine fechar esse arquivo assim que Popenretornar.
André Caron
4
-1: é um loop infinito se retcodefor 0. A verificação deve ser if retcode is not None. Não se deve produzir cadeias vazias (mesmo uma linha de vazio é, pelo menos, um símbolo de '\ n'): if line: yield line. Ligue p.stdout.close()no final.
JFS
2
Eu tentei o código com ls -l / dirname e quebra após o listando dois arquivos ao mesmo tempo há muito mais arquivos no diretório
Vasilis
3
@fuenfundachtzig: .readlines()não retornará até que toda a saída seja lida e, portanto, será interrompida para uma saída grande que não cabe na memória. Também para evitar a falta de dados em buffer após a saída do subprocesso, deve haver um análogo deif retcode is not None: yield from p.stdout.readlines(); break
jfs
67

A resposta da Vartec não lê todas as linhas, então fiz uma versão que:

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

O uso é o mesmo que a resposta aceita:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)
Max Ekman
fonte
6
você poderia usar return iter(p.stdout.readline, b'')em vez do loop while
JFS
2
Esse é um uso muito legal do iter, não sabia disso! Eu atualizei o código.
Max Ekman
Tenho certeza de que o stdout mantém toda a saída, é um objeto de fluxo com um buffer. Eu uso uma técnica muito semelhante para esgotar toda a saída restante após a conclusão de um Popen e, no meu caso, usando poll () e readline durante a execução para capturar a saída ao vivo também.
Max Ekman
Eu removi meu comentário enganoso. Posso confirmar, p.stdout.readline()pode retornar a saída em buffer anteriormente não vazia, mesmo que o processo filho já tenha sido encerrado ( p.poll()não está None).
jfs
Este código não funciona. Veja aqui stackoverflow.com/questions/24340877/…
thang
61

Esta é uma solução complicada, mas super simples , que funciona em várias situações:

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

Um arquivo temporário (aqui é tmp) é criado com a saída do comando e você pode ler a saída desejada.

Nota extra dos comentários: Você pode remover o arquivo tmp no caso de trabalho único. Se você precisar fazer isso várias vezes, não será necessário excluir o tmp.

os.remove('tmp')
Mehdi Saman Booy
fonte
5
Hacky, mas super simples + funciona em qualquer lugar .. pode combiná-lo com mktempa fazê-lo funcionar em situações de rosca, eu acho
Prakash Rajagaopal
2
Talvez o método mais rápido, mas é melhor adicionar os.remove('tmp')para torná-lo "sem arquivo".
XuMuK
@XuMuK Você está certo no caso de um emprego único. Se é um trabalho repetitivo talvez excluindo não é necessário
Mehdi Saman Booy
1
mau para a concorrência, mau para funções de reentrada, ruim para não deixar o sistema como ele era antes de começar (sem limpeza)
2mia
1
@ 2mia Obviamente, é fácil por uma razão! Se você deseja usar o arquivo como um tipo de memória compartilhada para leituras e gravações simultâneas, essa não é uma boa opção. Mas, por s.th. como ter a saída de um comando (por exemplo, ls ou find ou ...), pode ser uma escolha boa e rápida. Aliás, se você precisar de uma solução rápida para um problema simples, é o melhor que eu acho. Se você precisar de um pipeline, o subprocesso funcionará para você de forma mais eficiente.
Mehdi Saman Booy
44

Eu tive o mesmo problema, mas descobri uma maneira muito simples de fazer isso:

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

Espero que ajude

Nota: Esta solução é específica do Python3, pois subprocess.getoutput()não funciona no Python2

azhar22k
fonte
Como isso resolve o problema do OP? Por favor elabore.
RamenChef
4
Ele retorna a saída do comando como cordas, tão simples como isso
azhar22k
1
Obviamente, print é uma declaração do Python 2. Você deve descobrir que essa é uma resposta do Python 3.
2
As impressões @Dev são válidas para python 2. subprocess.getoutput não.
user48956
2
Para a maioria dos casos de uso, é isso que as pessoas provavelmente desejam: fácil de lembrar, não precisa decodificar os resultados etc. Obrigado.
bwv549
18

Você pode usar os seguintes comandos para executar qualquer comando do shell. Eu os usei no ubuntu.

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

Nota: Isso está obsoleto desde o python 2.6. Agora você deve usar subprocess.Popen. Abaixo está o exemplo

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")
Muhammad Hassan
fonte
2
Preterido desde a versão 2.6 - docs.python.org/2/library/os.html#os.popen
Filippo Vitale
1
@FilippoVitale Thanks. Eu não sabia que isso foi preterido.
Muhammad Hassan
1
De acordo com raspberrypi.stackexchange.com/questions/71547/... os.popen() está obsoleta em Python 2.6, mas é não depreciado em Python 3.x, uma vez que em 3.x ele é implementado usando subprocess.Popen().
JL
12

Sua milhagem pode variar, tentei a rotação de @ senderle na solução da Vartec no Windows no Python 2.6.5, mas estava recebendo erros e nenhuma outra solução funcionou. Meu erro foi: WindowsError: [Error 6] The handle is invalid.

Eu descobri que tinha que atribuir o PIPE a cada identificador para que ele retornasse a saída que eu esperava - o seguinte funcionou para mim.

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

e chame assim, ( [0]obtém o primeiro elemento da tupla stdout):

run_command('tracert 11.1.0.1')[0]

Depois de aprender mais, acredito que preciso desses argumentos de pipe, porque estou trabalhando em um sistema personalizado que usa identificadores diferentes, então tive que controlar diretamente todos os padrões.

Para parar os pop-ups do console (com Windows), faça o seguinte:

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')
Aaron Hall
fonte
1
Interessante - isso deve ser uma coisa do Windows. Vou adicionar uma nota apontando para isso, caso as pessoas estejam recebendo erros semelhantes.
Senderle
use em DEVNULLvez de subprocess.PIPEse você não escrever / ler de um tubo, caso contrário, poderá interromper o processo filho.
jfs
10

Eu tinha um sabor ligeiramente diferente do mesmo problema com os seguintes requisitos:

  1. Capture e retorne mensagens STDOUT à medida que elas se acumulam no buffer STDOUT (ou seja, em tempo real).
    • O @vartec resolveu isso Pythonically com o uso de geradores e a
      palavra-chave 'yield' acima
  2. Imprimir todas as linhas STDOUT ( mesmo que o processo saia antes que o buffer STDOUT possa ser lido completamente )
  3. Não desperdice os ciclos da CPU pesquisando o processo em alta frequência
  4. Verifique o código de retorno do subprocesso
  5. Imprima STDERR (separado de STDOUT) se obtivermos um código de retorno de erro diferente de zero.

Combinei e aprimorei as respostas anteriores para criar o seguinte:

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

Este código seria executado da mesma forma que as respostas anteriores:

for line in run_command(cmd):
    print(line)
The Aelfinn
fonte
1
Você se importa em explicar como a adição de suspensão (.1) não desperdiça ciclos de CPU?
Moataz Elmasry
2
Se continuássemos a chamada p.poll()sem dormir entre as chamadas, desperdiçaríamos ciclos de CPU chamando essa função milhões de vezes. Em vez disso, "estrangulamos" nosso loop dizendo ao sistema operacional que não precisamos nos incomodar pelo próximo 1/10 de segundo, para que ele possa realizar outras tarefas. (É possível que p.poll () também durma, tornando nossa declaração de sono redundante).
O Aelfinn
5

Dividir o comando inicial para o subprocesspode ser complicado e complicado.

Use shlex.split()para se ajudar.

Comando de amostra

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

O código

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

Sem shlex.split()o código ficaria da seguinte maneira

res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    '5 years ago', 
    '--until', 
    '2 year ago'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'
Artur Barseghyan
fonte
1
shlex.split()é uma conveniência, especialmente se você não sabe exatamente como funciona a citação no shell; mas converter manualmente essa sequência na lista ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']não é difícil, se você entender as aspas.
tripleee
4

Se você precisar executar um comando shell em vários arquivos, isso fez o truque para mim.

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

Edit: Acabei de ver a solução de Max Persson com a sugestão de JF Sebastian. Fui em frente e incorporou isso.

Ethan Strider
fonte
Popenaceita uma string, mas você precisa shell=True, ou uma lista de argumentos; nesse caso, você deve passar em ['nm', filename]vez de uma string. O último é preferível porque o shell adiciona complexidade sem fornecer nenhum valor aqui. Passar uma string sem shell=Trueaparentemente funcionar no Windows, mas isso pode mudar em qualquer próxima versão do Python.
tripleee
2

De acordo com @senderle, se você usa python3.6 como eu:

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

Agirá exatamente como você executa o comando no bash

Neo li
fonte
Você está reinventando os argumentos da palavra-chave check=True, universal_newlines=True. Em outras palavras, subprocess.run()já faz tudo o que seu código faz.
tripleee 12/06/19
1

Se você usar o subprocessmódulo python, poderá lidar com o STDOUT, STDERR e o código de comando de retorno separadamente. Você pode ver um exemplo para a implementação completa do chamador de comando. Claro que você pode estendê-lo try..exceptse quiser.

A função abaixo retorna os códigos STDOUT, STDERR e Return, para que você possa manipulá-los no outro script.

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
fonte
Outra má reimplementação de subprocess.run(). Não reinvente a roda.
tripleee
0

por exemplo, execute ('ls -ahl') diferenciou três / quatro retornos possíveis e plataformas de SO:

  1. sem saída, mas execute com sucesso
  2. linha vazia de saída, execute com sucesso
  3. Falha na execução
  4. produzir algo, executar com sucesso

função abaixo

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 ""
Jerry T
fonte
0

A saída pode ser redirecionada para um arquivo de texto e depois lida novamente.

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))
SENHOR
fonte
Claro que pode, mas por que você gostaria; e por que você usaria o shell em vez de passar stdout=temp_file?
tripleee
Na verdade, de uma maneira geral, você está certo, mas, no meu exemplo, ecls.exeparece implantar outra ferramenta de linha de comando; portanto, a maneira simples não funcionava às vezes.
MR
0

acabou de escrever um pequeno script bash para fazer isso usando 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
fonte