Ejecutar el comando Shell y capturar la salida
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'
En todas las versiones de Python mantenidas oficialmente, el enfoque más sencillo es utilizar la subprocess.check_output
función:
>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
check_output
ejecuta 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 run
o Popen
. Si desea ejecutar comandos de shell complejos, consulte la nota shell=True
al final de esta respuesta.
La check_output
funció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 , run
la 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 subprocess
módulo. Para capturar el resultado de un programa, pase la subprocess.PIPE
bandera al stdout
argumento de palabra clave. Luego acceda al stdout
atributo del CompletedProcess
objeto 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 bytes
objeto, 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 bytes
objeto al input
argumento 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.stdout
junto con la salida normal). Si desea run
generar una excepción cuando el proceso devuelva un código de salida distinto de cero, puede pasar check=True
. (O puede verificar el returncode
atributo result
anterior). Cuando la seguridad no es una preocupación, también puede ejecutar comandos de shell más complejos pasándolos shell=True
como 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 run
esta 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 run
solo 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_output
funció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_output
equivale a ejecutar run
con check=True
y stdout=PIPE
y devolver solo el stdout
atributo.
Puede pasar stderr=subprocess.STDOUT
para 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=True
como se describe al final de esta respuesta.
Si necesita canalizar stderr
o pasar información al proceso, check_output
no estará a la altura de la tarea. Vea los Popen
ejemplos 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_output
la que run
proporcionamos, tendrá que trabajar directamente con Popen
objetos, que encapsulan la API de bajo nivel para los subprocesos.
El Popen
constructor 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.split
puede ayudar a analizar cadenas en listas con el formato adecuado. Popen
Los 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 communicate
es 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
, communicate
tambié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
, stderr
y stdin
all en PIPE
(o DEVNULL
) para comenzar communicate
a 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 communicate
son 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=True
argumento
Normalmente, cada llamada a run
, check_output
o al Popen
constructor 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 .
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 subprocess
módulo en su lugar:
from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response
python3
ofertas subprocess.getoutput()
:
import subprocess
output = subprocess.getoutput("ls -l")
print(output)