Ejecutar el comando Shell y capturar la salida

Resuelto Silver Light asked hace 13 años • 25 respuestas

Quiero escribir una función que ejecute un comando de shell y devuelva su salida como una cadena , sin importar si es un mensaje de error o de éxito. Sólo quiero obtener el mismo resultado que habría obtenido con la línea de comando.

¿Cuál sería un ejemplo de código que haría tal cosa?

Por ejemplo:

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'
Silver Light avatar Jan 21 '11 21:01 Silver Light
Aceptado

En todas las versiones de Python mantenidas oficialmente, el enfoque más sencillo es utilizar la subprocess.check_outputfunción:

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

check_outputejecuta un único programa que solo toma argumentos como entrada. 1 Devuelve el resultado exactamente como se imprimió en stdout. Si necesita escribir información en stdin, salte a las secciones runo Popen. Si desea ejecutar comandos de shell complejos, consulte la nota shell=Trueal final de esta respuesta.

La check_outputfunción funciona en todas las versiones mantenidas oficialmente de Python. Pero para versiones más recientes, hay disponible un enfoque más flexible.

Versiones modernas de Python (3.5 o superior):run

Si está utilizando Python 3.5+ y no necesita compatibilidad con versiones anteriores , runla documentación oficial recomienda la nueva función para la mayoría de las tareas. Proporciona una API muy general de alto nivel para el subprocessmódulo. Para capturar el resultado de un programa, pase la subprocess.PIPEbandera al stdoutargumento de palabra clave. Luego acceda al stdoutatributo del CompletedProcessobjeto devuelto:

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

El valor de retorno es un bytesobjeto, por lo que si desea una cadena adecuada, la necesitará decode. Suponiendo que el proceso llamado devuelve una cadena codificada en UTF-8:

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

Todo esto se puede comprimir en una sola línea si se desea:

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

Si desea pasar información al proceso stdin, puede pasar un bytesobjeto al inputargumento de la palabra clave:

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

Puede capturar errores pasando stderr=subprocess.PIPE(capturar a result.stderr) o stderr=subprocess.STDOUT(capturar result.stdoutjunto con la salida normal). Si desea rungenerar una excepción cuando el proceso devuelva un código de salida distinto de cero, puede pasar check=True. (O puede verificar el returncodeatributo resultanterior). Cuando la seguridad no es una preocupación, también puede ejecutar comandos de shell más complejos pasándolos shell=Truecomo se describe al final de esta respuesta.

Las versiones posteriores de Python simplifican aún más lo anterior. En Python 3.7+, la frase anterior se puede escribir así:

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

Usar runesta forma añade un poco de complejidad, en comparación con la antigua forma de hacer las cosas. Pero ahora puedes hacer casi cualquier cosa que necesites hacer runsolo con la función.

Versiones anteriores de Python (3-3.4): más información sobrecheck_output

Si está utilizando una versión anterior de Python o necesita una modesta compatibilidad con versiones anteriores, puede utilizar la check_outputfunción como se describe brevemente anteriormente. Ha estado disponible desde Python 2.7.

subprocess.check_output(*popenargs, **kwargs)  

Toma los mismos argumentos que Popen(ver más abajo) y devuelve una cadena que contiene la salida del programa. El comienzo de esta respuesta tiene un ejemplo de uso más detallado. En Python 3.5+, check_outputequivale a ejecutar runcon check=Truey stdout=PIPEy devolver solo el stdoutatributo.

Puede pasar stderr=subprocess.STDOUTpara asegurarse de que los mensajes de error se incluyan en el resultado devuelto. Cuando la seguridad no es una preocupación, también puede ejecutar comandos de shell más complejos pasándolos shell=Truecomo se describe al final de esta respuesta.

Si necesita canalizar stderro pasar información al proceso, check_outputno estará a la altura de la tarea. Vea los Popenejemplos a continuación en ese caso.

Aplicaciones complejas y versiones heredadas de Python (2.6 y anteriores):Popen

Si necesita una compatibilidad profunda con versiones anteriores, o si necesita una funcionalidad más sofisticada que check_outputla que runproporcionamos, tendrá que trabajar directamente con Popenobjetos, que encapsulan la API de bajo nivel para los subprocesos.

El Popenconstructor acepta un solo comando sin argumentos o una lista que contiene un comando como primer elemento, seguido de cualquier número de argumentos, cada uno como un elemento separado en la lista. shlex.splitpuede ayudar a analizar cadenas en listas con el formato adecuado. PopenLos objetos también aceptan una serie de argumentos diferentes para la gestión de E/S del proceso y la configuración de bajo nivel.

Enviar entradas y capturar salidas communicatees casi siempre el método preferido. Como en:

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

O

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

Si lo configura stdin=PIPE, communicatetambién le permite pasar datos al proceso a través de 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

Tenga en cuenta la respuesta de Aaron Hall , que indica que en algunos sistemas, es posible que deba configurar stdout, stderry stdinall en PIPE(o DEVNULL) para comenzar communicatea trabajar.

En algunos casos excepcionales, es posible que necesite una captura de resultados compleja en tiempo real. La respuesta de Vartec sugiere un camino a seguir, pero otros métodos communicateson propensos a estancarse si no se usan con cuidado.

Al igual que con todas las funciones anteriores, cuando la seguridad no es una preocupación, puede ejecutar comandos de shell más complejos pasando shell=True.

Notas

1. Ejecutar comandos de shell: el shell=Trueargumento

Normalmente, cada llamada a run, check_outputo al Popenconstructor ejecuta un único programa . Eso significa que no hay pipas elegantes estilo bash. Si desea ejecutar comandos de shell complejos, puede pasar shell=True, que admite las tres funciones. Por ejemplo:

>>> subprocess.check_output('cat books/* | wc', shell=True, text=True)
' 1299377 17005208 101299376\n'

Sin embargo, hacer esto genera preocupaciones de seguridad . Si está haciendo algo más que secuencias de comandos ligeras, sería mejor llamar a cada proceso por separado y pasar la salida de cada uno como entrada al siguiente, a través de

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

O

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

La tentación de conectar tuberías directamente es fuerte; resistelo. De lo contrario, probablemente verás puntos muertos o tendrás que hacer cosas complicadas como esta .

senderle avatar Jan 21 '2011 15:01 senderle

Esto es mucho más fácil, pero sólo funciona en Unix (incluido Cygwin) y Python2.7.

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

Devuelve una tupla con (valor_retorno, salida).

Para obtener una solución que funcione tanto en Python2 como en Python3, utilice el subprocessmódulo en su lugar:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response
byte_array avatar Feb 13 '2012 19:02 byte_array

python3ofertas subprocess.getoutput():

import subprocess
output = subprocess.getoutput("ls -l")
print(output)
azhar22k avatar Nov 12 '2016 19:11 azhar22k