Almacenar la salida del subproceso. Abrir llamada en una cadena

300

Estoy tratando de hacer una llamada al sistema en Python y almacenar el resultado en una cadena que puedo manipular en el programa Python.

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

He intentado algunas cosas, incluidas algunas de las sugerencias aquí:

Recuperando la salida de subprocess.call ()

Pero sin suerte.

marca
fuente
3
Siempre es bueno publicar el código real que ejecutó y el rastreo real o el comportamiento inesperado para preguntas concretas como esta. Por ejemplo, no sé qué trató de hacer para obtener la salida y sospecho que en realidad no llegó tan lejos para comenzar: habría recibido un error al no encontrar el archivo "ntpq -p", que es una parte diferente de el problema del que estás preguntando.
Mike Graham

Respuestas:

467

En Python 2.7 o Python 3

En lugar de crear un Popenobjeto directamente, puede usar la subprocess.check_output()función para almacenar la salida de un comando en una cadena:

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

En Python 2.4-2.6

Usa el communicatemétodo.

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

out es lo que quieres

Nota importante sobre las otras respuestas.

Tenga en cuenta cómo pasé el comando. El "ntpq -p"ejemplo plantea otro asunto. Como Popenno invoca el shell, usaría una lista de comandos y opciones— ["ntpq", "-p"].

Mike Graham
fuente
3
En este caso, ¿Python espera a que finalice esta llamada al sistema? ¿O es necesario llamar explícitamente a la función wait / waitpid?
Ninguno Escriba el
77
@NoneType, Popen.communicateno regresa hasta que el proceso haya finalizado.
Mike Graham el
10
si desea obtener flujo de error agregue stderr:p = subprocess.Popen(["ntpq", "-p"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Timofey
82
Cuidado, subprocess.check_output()devuelve un bytesobjeto, no un str. Si todo lo que quiere hacer es imprimir el resultado, eso no hará ninguna diferencia. Pero si desea utilizar un método como myString.split("\n")el resultado, primero deberá decodificar el bytesobjeto: subprocess.check_output(myParams).decode("utf-8")por ejemplo.
TanguyP
17
agregar universal_newlines=Truecomo parámetro me ayudó en Python 3 a obtener el objeto de cadena. Si universal_newlines es True, se abren en modo texto con codificación predeterminada. De lo contrario, se abren como secuencias binarias.
Jonathan Komar
38

Esto funcionó para mí para redirigir stdout (stderr se puede manejar de manera similar):

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

Si no funciona para usted, especifique exactamente el problema que está teniendo.

Eli Bendersky
fuente
3
Esto produce algún objeto extraño. Cuando lo convierto a cadena, se escapa como espacios en blanco \n.
Tomáš Zato - Restablece a Mónica el
1
Tenga en cuenta que esto no verifica si el subproceso se ejecutó correctamente. Probablemente quieras comprobar eso pipe.returncode == 0después de la última línea también.
thakis
la razón por la que esto funciona es porque Popendevuelve una tupla de stdouty stderr, así que cuando accedes [0]solo estás agarrando el stdout. También podría hacer text, err = pipe.communicate()y luego texttendrá lo que espera
Jona
23

Asumiendo que pwdes solo un ejemplo, así es como puedes hacerlo:

import subprocess

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

Consulte la documentación del subproceso para otro ejemplo y más información.

Mark Byers
fuente
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]

En el constructor de Popen, si shell es True , debe pasar el comando como una cadena en lugar de como una secuencia. De lo contrario, simplemente divida el comando en una lista:

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

Si necesita leer también el error estándar, en la inicialización de Popen, puede establecer stderr en subprocess.PIPE o en 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
fuente
increíble, esto es lo que estaba buscando
kRazzy R
14

para Python 2.7+ la respuesta idiomática es usar subprocess.check_output()

También debe tener en cuenta el manejo de argumentos al invocar un subproceso, ya que puede ser un poco confuso ...

Si args es solo un comando sin ningún argumento propio (o lo ha shell=Trueconfigurado), puede ser una cadena. De lo contrario, debe ser una lista.

por ejemplo ... para invocar el lscomando, está bien:

from subprocess import check_call
check_call('ls')

asi es esto:

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

sin embargo, si desea pasar algunos argumentos al comando de shell, no puede hacer esto:

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

en su lugar, debe pasarlo como una lista:

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

La shlex.split()función a veces puede ser útil para dividir una cadena en una sintaxis tipo shell antes de crear un subproceso ... como este:

from subprocess import check_call
import shlex
check_call(shlex.split('ls -al'))
Corey Goldberg
fuente
Cinco años después, esta pregunta sigue recibiendo mucho amor. Gracias por la actualización 2.7+, Corey!
Mark
11

Esto funciona perfectamente para mi:

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

Esto fue perfecto para mi. Obtendrá el código de retorno, stdout y stderr en una 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 ejemplo:

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

La respuesta aceptada sigue siendo buena, solo algunas observaciones sobre las nuevas funciones. Desde python 3.6, puede manejar la codificación directamente check_output, consulte la documentación . Esto devuelve un objeto de cadena ahora:

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

En python 3.7, capture_outputse agregó un parámetro a subprocess.run (), que realiza parte del manejo de Popen / PIPE por nosotros, consulte los documentos de python :

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

En este caso, solo tendrá un elemento en la lista.

Alex
fuente
77
os.popenestá en desuso a favor del subprocessmódulo.
Mike Graham
3
Esto fue bastante útil para el administrador en una caja antigua que usaba la serie 2.2.X de Python.
Neil McF
4

Escribí una pequeña función basada en las otras respuestas aquí:

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

En Python 3.7 capture_outputse introdujo un nuevo argumento de palabra clave para subprocess.run. Habilitando lo corto y simple:

import subprocess

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

Esta es una solución de línea

Kirti Kedia
fuente
0

Lo siguiente captura stdout y stderr del proceso en una sola variable. Es compatible con Python 2 y 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

Si su comando es una cadena en lugar de una matriz, prefije esto con:

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

Para python 3.5 puse una función basada en la respuesta anterior. Se puede eliminar el registro, aunque es bueno tener

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

Utilizar el ckeck_outputmétodo desubprocess

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

Finalmente analizar la cadena

for line in res.splitlines():

Espero que ayude, feliz codificación

Salman Mushtaq
fuente