Armazene a saída da chamada subprocess.Popen em uma sequência

300

Estou tentando fazer uma chamada de sistema em Python e armazenar a saída em uma string que eu possa manipular no programa Python.

#!/usr/bin/python
import subprocess
p2 = subprocess.Popen("ntpq -p")

Eu tentei algumas coisas, incluindo algumas das sugestões aqui:

Recuperando a saída de subprocess.call ()

mas sem sorte.

Marca
fonte
3
É sempre bom postar o código real que você executou e o rastreamento real ou comportamento inesperado para perguntas concretas como essa. Por exemplo, eu não sei o que você tentou fazer para obter a saída e desconfio que você não chegou tão longe assim - você teria recebido um erro ao não encontrar o arquivo "ntpq -p", o que é uma parte diferente de o problema do que você está perguntando.
Mike Graham

Respostas:

467

No Python 2.7 ou Python 3

Em vez de criar um Popenobjeto diretamente, você pode usar a subprocess.check_output()função para armazenar a saída de um comando em uma string:

from subprocess import check_output
out = check_output(["ntpq", "-p"])

No Python 2.4-2.6

Use o communicatemétodo

import subprocess
p = subprocess.Popen(["ntpq", "-p"], stdout=subprocess.PIPE)
out, err = p.communicate()

out é o que você quer.

Nota importante sobre as outras respostas

Observe como passei no comando. O "ntpq -p"exemplo traz outra questão. Como Popennão invoca o shell, você usaria uma lista do comando e opções— ["ntpq", "-p"].

Mike Graham
fonte
3
Nesse caso, o python espera a conclusão dessa chamada do sistema? Ou é necessário chamar explicitamente a função wait / waitpid?
NoneType
7
@NoneType, Popen.communicatenão retorna até o processo terminar.
Mike Graham
10
se você deseja obter o fluxo de erro, adicione stderr:p = subprocess.Popen(["ntpq", "-p"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Timofey
82
Cuidado, subprocess.check_output()retorna um bytesobjeto, não a str. Se tudo o que você deseja fazer é imprimir o resultado, isso não fará nenhuma diferença. Mas se você quiser usar um método como myString.split("\n")o resultado, precisará decodificar o bytesobjeto: subprocess.check_output(myParams).decode("utf-8")por exemplo.
precisa saber é o seguinte
17
adicionar universal_newlines=Truecomo parâmetro me ajudou no Python 3 a obter o objeto string. Se universal_newlines for True, eles serão abertos no modo de texto com codificação padrão. Caso contrário, eles são abertos como fluxos binários.
Jonathan Komar
38

Isso funcionou para mim no redirecionamento do stdout (o stderr pode ser tratado da mesma forma):

from subprocess import Popen, PIPE
pipe = Popen(path, stdout=PIPE)
text = pipe.communicate()[0]

Se não funcionar, especifique exatamente o problema que está ocorrendo.

Eli Bendersky
fonte
3
Isso produz algum objeto estranho. Quando o converto em string, ele escapa como o espaço em branco \n.
Tomáš Zato - Restabelece Monica
1
Observe que isso não verifica se o subprocesso foi executado corretamente. Você provavelmente deseja verificar isso pipe.returncode == 0após a última linha também.
22418 thakis
a razão pela qual isso funciona é porque Popenretorna uma tupla de stdoute stderr, portanto, quando você acessa, [0]está apenas agarrando o stdout. Você também pode fazer text, err = pipe.communicate()e, em seguida, textterá o que você espera
Jona
23

Supondo que pwdseja apenas um exemplo, é assim que você pode fazer:

import subprocess

p = subprocess.Popen("pwd", stdout=subprocess.PIPE)
result = p.communicate()[0]
print result

Consulte a documentação do subprocesso para outro exemplo e mais informações.

Mark Byers
fonte
22

subprocess.Popen: http://docs.python.org/2/library/subprocess.html#subprocess.Popen

import subprocess

command = "ntpq -p"  # the shell command
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=None, shell=True)

#Launch the shell command:
output = process.communicate()

print output[0]

No construtor Popen, se o shell for True , você deve passar o comando como uma string e não como uma sequência. Caso contrário, basta dividir o comando em uma lista:

command = ["ntpq", "-p"]  # the shell command
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=None)

Se você precisar ler também o erro padrão, na inicialização do Popen, poderá definir stderr como subprocess.PIPE ou subprocess.STDOUT :

import subprocess

command = "ntpq -p"  # the shell command
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

#Launch the shell command:
output, error = process.communicate()
Paolo Rovelli
fonte
este impressionante é o que eu estava procurando
Krazzy R
14

para Python 2.7+, a resposta idiomática é usar subprocess.check_output()

Você também deve observar a manipulação de argumentos ao chamar um subprocesso, pois pode ser um pouco confuso ....

Se args é apenas um comando sem args próprio (ou você shell=Trueconfigurou), pode ser uma sequência. Caso contrário, deve ser uma lista.

por exemplo ... para chamar o lscomando, tudo bem:

from subprocess import check_call
check_call('ls')

então é isso:

from subprocess import check_call
check_call(['ls',])

no entanto, se você quiser passar alguns argumentos para o comando shell, não poderá fazer isso:

from subprocess import check_call
check_call('ls -al')

em vez disso, você deve passá-lo como uma lista:

from subprocess import check_call
check_call(['ls', '-al'])

a shlex.split()função às vezes pode ser útil para dividir uma string em sintaxe semelhante a shell antes de criar um subprocesso ... assim:

from subprocess import check_call
import shlex
check_call(shlex.split('ls -al'))
Corey Goldberg
fonte
Cinco anos depois, essa questão ainda está recebendo muito amor. Obrigado pela atualização 2.7+, Corey!
Mark
11

Isso funciona perfeitamente para mim:

import subprocess
try:
    #prints results and merges stdout and std
    result = subprocess.check_output("echo %USERNAME%", stderr=subprocess.STDOUT, shell=True)
    print result
    #causes error and merges stdout and stderr
    result = subprocess.check_output("copy testfds", stderr=subprocess.STDOUT, shell=True)
except subprocess.CalledProcessError, ex: # error code <> 0 
    print "--------error------"
    print ex.cmd
    print ex.message
    print ex.returncode
    print ex.output # contains stdout and stderr together 
Patrick Wolf
fonte
9

Isso foi perfeito para mim. Você receberá o código de retorno, stdout e stderr em uma tupla.

from subprocess import Popen, PIPE

def console(cmd):
    p = Popen(cmd, shell=True, stdout=PIPE)
    out, err = p.communicate()
    return (p.returncode, out, err)

Por exemplo:

result = console('ls -l')
print 'returncode: %s' % result[0]
print 'output: %s' % result[1]
print 'error: %s' % result[2]
gravmatt
fonte
6

A resposta aceita ainda é boa, apenas algumas observações sobre os recursos mais recentes. Desde o python 3.6, você pode lidar com a codificação diretamente check_output, consulte a documentação . Isso retorna um objeto de string agora:

import subprocess 
out = subprocess.check_output(["ls", "-l"], encoding="utf-8")

No python 3.7, um parâmetro capture_outputfoi adicionado ao subprocess.run (), que faz parte do processamento do Popen / PIPE para nós, consulte os documentos do python :

import subprocess 
p2 = subprocess.run(["ls", "-l"], capture_output=True, encoding="utf-8")
p2.stdout
ynux
fonte
4
 import os   
 list = os.popen('pwd').read()

Nesse caso, você terá apenas um elemento na lista.

Alex
fonte
7
os.popenfoi descontinuado em favor do subprocessmódulo.
Mike Graham
3
Isso foi bastante útil para o administrador em uma caixa antiga usando a série 2.2.X do Python.
Neil McF
4

Eu escrevi uma pequena função com base nas outras respostas aqui:

def pexec(*args):
    return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0].rstrip()

Uso:

changeset = pexec('hg','id','--id')
branch = pexec('hg','id','--branch')
revnum = pexec('hg','id','--num')
print('%s : %s (%s)' % (revnum, changeset, branch))
mpen
fonte
4

No Python 3.7, um novo argumento de palavra-chave capture_outputfoi introduzido para subprocess.run. Habilitando o curto e simples:

import subprocess

p = subprocess.run("echo 'hello world!'", capture_output=True, shell=True, encoding="utf8")
assert p.stdout == 'hello world!\n'
erb
fonte
1
import subprocess
output = str(subprocess.Popen("ntpq -p",shell = True,stdout = subprocess.PIPE, 
stderr = subprocess.STDOUT).communicate()[0])

Esta é uma solução de linha

Kirti Kedia
fonte
0

O seguinte captura stdout e stderr do processo em uma única variável. É compatível com Python 2 e 3:

from subprocess import check_output, CalledProcessError, STDOUT

command = ["ls", "-l"]
try:
    output = check_output(command, stderr=STDOUT).decode()
    success = True 
except CalledProcessError as e:
    output = e.output.decode()
    success = False

Se o seu comando for uma string em vez de uma matriz, prefixe-o com:

import shlex
command = shlex.split(command)
Zags
fonte
0

Para python 3.5, eu coloquei a função com base na resposta anterior. O registro pode ser removido, mas é bom ter

import shlex
from subprocess import check_output, CalledProcessError, STDOUT


def cmdline(command):
    log("cmdline:{}".format(command))
    cmdArr = shlex.split(command)
    try:
        output = check_output(cmdArr,  stderr=STDOUT).decode()
        log("Success:{}".format(output))
    except (CalledProcessError) as e:
        output = e.output.decode()
        log("Fail:{}".format(output))
    except (Exception) as e:
        output = str(e);
        log("Fail:{}".format(e))
    return str(output)


def log(msg):
    msg = str(msg)
    d_date = datetime.datetime.now()
    now = str(d_date.strftime("%Y-%m-%d %H:%M:%S"))
    print(now + " " + msg)
    if ("LOG_FILE" in globals()):
        with open(LOG_FILE, "a") as myfile:
            myfile.write(now + " " + msg + "\n")
Pavel Niedoba
fonte
0

Use o ckeck_outputmétodo desubprocess

import subprocess
address = 192.168.x.x
res = subprocess.check_output(['ping', address, '-c', '3'])

Finalmente analise a string

for line in res.splitlines():

Espero que ajude, codificação feliz

Salman Mushtaq
fonte