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. Solo 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'
python
shell
subprocess
Luz plateada
fuente
fuente
Respuestas:
La respuesta a esta pregunta depende de la versión de Python que esté utilizando. El enfoque más simple es usar la
subprocess.check_output
función:check_output
ejecuta un solo programa que toma solo argumentos como entrada. 1 Devuelve el resultado exactamente como se imprimióstdout
. Si necesita escribir una entradastdin
, salte a las seccionesrun
oPopen
. Si desea ejecutar comandos de shell complejos, vea la notashell=True
al final de esta respuesta.La
check_output
función funciona en casi todas las versiones de Python que todavía se usan ampliamente (2.7+). 2 Pero para versiones más recientes, ya no es el enfoque recomendado.Versiones modernas de Python (3.5 o superior):
run
Si está utilizando Python 3.5 o superior, y no necesita compatibilidad con versiones anteriores , se recomienda la nueva
run
función . Proporciona una API muy general de alto nivel para elsubprocess
módulo. Para capturar la salida de un programa, pase lasubprocess.PIPE
bandera alstdout
argumento de la palabra clave. Luego acceda alstdout
atributo delCompletedProcess
objeto devuelto :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:Todo esto se puede comprimir en una sola línea:
Si desea pasar la entrada al proceso
stdin
, pase unbytes
objeto alinput
argumento de la palabra clave:Puede capturar errores pasando
stderr=subprocess.PIPE
(capturar aresult.stderr
) ostderr=subprocess.STDOUT
(capturarresult.stdout
junto con la salida regular). Cuando la seguridad no es una preocupación, también puede ejecutar comandos de shell más complejos pasandoshell=True
como se describe en las notas a continuación.Esto agrega un poco de complejidad, en comparación con la antigua forma de hacer las cosas. Pero creo que vale la pena: ahora puede hacer casi cualquier cosa que necesite hacer solo con la
run
función.Versiones anteriores de Python (2.7-3.4):
check_output
Si está utilizando una versión anterior de Python, o necesita una modesta compatibilidad con versiones anteriores, probablemente pueda usar la
check_output
función como se describe brevemente anteriormente. Ha estado disponible desde Python 2.7.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 y superior,check_output
es equivalente a ejecutarrun
concheck=True
ystdout=PIPE
, y devolver solo elstdout
atributo.Puede pasar
stderr=subprocess.STDOUT
a garantizar que los mensajes de error se incluyen en la salida devuelta - pero en algunas versiones de Python que pasastderr=subprocess.PIPE
acheck_output
puede causar bloqueos . Cuando la seguridad no es una preocupación, también puede ejecutar comandos de shell más complejos pasandoshell=True
como se describe en las notas a continuación.Si necesita canalizar
stderr
o pasar la entrada al proceso,check_output
no estará a la altura de la tarea. Vea losPopen
ejemplos a continuación en ese caso.Aplicaciones complejas y versiones heredadas de Python (2.6 y siguientes):
Popen
Si necesita una profunda compatibilidad con versiones anteriores, o si necesita una funcionalidad más sofisticada que la que
check_output
proporciona, tendrá que trabajar directamente conPopen
objetos, que encapsulan la API de bajo nivel para subprocesos.El
Popen
constructor acepta un solo comando sin argumentos, o una lista que contiene un comando como su 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 gran cantidad de argumentos diferentes para la gestión de IO de procesos y la configuración de bajo nivel.Para enviar entrada y capturar salida,
communicate
casi siempre es el método preferido. Como en:O
Si configura
stdin=PIPE
,communicate
también le permite pasar datos al proceso a través destdin
:Nota respuesta de Aaron Hall , lo que indica que en algunos sistemas, es posible que necesite conjunto
stdout
,stderr
ystdin
que todosPIPE
(oDEVNULL
) para llegarcommunicate
al trabajo en absoluto.En algunos casos raros, es posible que necesite una captura de salida compleja y en tiempo real. La respuesta de Vartec sugiere un camino a seguir, pero otros métodos que no
communicate
son propensos a puntos muertos 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 al pasar
shell=True
.Notas
1. Ejecutar comandos de shell: el
shell=True
argumentoNormalmente, cada llamada a
run
,check_output
o elPopen
constructor ejecuta un solo programa . Eso significa que no hay pipas elegantes de estilo bash. Si desea ejecutar comandos de shell complejos, puede pasarshell=True
, que admiten las tres funciones.Sin embargo, hacerlo plantea problemas de seguridad . Si está haciendo algo más que secuencias de comandos ligeras, es mejor que llame a cada proceso por separado y pase la salida de cada uno como entrada al siguiente, a través de
O
La tentación de conectar directamente las tuberías es fuerte; resistelo. De lo contrario, es probable que veas puntos muertos o que tengas que hacer cosas extrañas como esta .
2. Consideraciones Unicode
check_output
devuelve una cadena en Python 2, pero unbytes
objeto en Python 3. Vale la pena tomarse un momento para aprender sobre Unicode si aún no lo ha hecho.fuente
check_output()
ycommunicate()
tienes que esperar hasta que el proceso haya terminado, conpoll()
lo que obtienes la salida tal como viene. Realmente depende de lo que necesites.out
fue de tipo<class 'bytes'>
para mí. Para obtener el resultado como una cadena, tuve que decodificarlo antes de imprimir así:out.decode("utf-8")
shell=True
? Esto funciona para mi. No necesitasshlex.split
cuando pasasshell=True
.shlex.split
es para comandos que no son de shell. Creo que voy a sacar esa parte porque esto está enturbiando las aguas.universal_newlines=True
que le permite pasar y sacar cadenas Unicode en la codificación predeterminada del sistema. En 3.7 se cambió el nombre por el más sensatotext=True
.encoding
parámetro de ensubprocess.run
lugar de usarresult.stdout.decode('utf-8')
, puede usarsubprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8')
.Esto es mucho más fácil, pero solo funciona en Unix (incluido Cygwin) y Python2.7.
Devuelve una tupla con (return_value, output).
Para una solución que funcione tanto en Python2 como en Python3, use el
subprocess
módulo en su lugar:fuente
Algo como eso:
Tenga en cuenta que estoy redirigiendo stderr a stdout, puede que no sea exactamente lo que desea, pero también quiero mensajes de error.
Esta función produce línea por línea a medida que avanzan (normalmente tendría que esperar a que finalice el subproceso para obtener la salida como un todo).
Para su caso, el uso sería:
fuente
wait
y lascall
funciones.PIPE
valorstdin
y cerrar ese archivo tan pronto comoPopen
regrese.retcode
es0
. El cheque debería serif retcode is not None
. No se debe producir cadenas vacías (incluso una línea vacía es al menos un símbolo '\ n'):if line: yield line
. Llamap.stdout.close()
al final..readlines()
no volverá hasta que se lea toda la salida y, por lo tanto, se rompe para una salida grande que no cabe en la memoria. También para evitar la falta de datos almacenados en el búfer después de que salga el subproceso, debe haber un análogo deif retcode is not None: yield from p.stdout.readlines(); break
La respuesta de Vartec no lee todas las líneas, así que hice una versión que sí:
El uso es el mismo que la respuesta aceptada:
fuente
return iter(p.stdout.readline, b'')
lugar del bucle whilep.stdout.readline()
puede devolver la salida previamente vacía no almacenada, incluso si el proceso secundario ya ha salido (p.poll()
no lo estáNone
).Esta es una solución difícil pero súper simple que funciona en muchas situaciones:
Se crea un archivo temporal (aquí es tmp) con la salida del comando y puede leer de él la salida deseada.
Nota adicional de los comentarios: puede eliminar el archivo tmp en el caso de un trabajo único. Si necesita hacer esto varias veces, no es necesario eliminar el tmp.
fuente
mktemp
para que funcione en situaciones de rosca, supongoos.remove('tmp')
para que sea "sin archivos".Tuve el mismo problema pero descubrí una forma muy simple de hacer esto:
Espero que ayude
Nota: Esta solución es específica de Python3 ya
subprocess.getoutput()
que no funciona en Python2fuente
Puede usar los siguientes comandos para ejecutar cualquier comando de shell. Los he usado en ubuntu.
Nota: Esto está en desuso desde python 2.6. Ahora debes usar
subprocess.Popen
. Debajo está el ejemplofuente
os.popen()
está en desuso en Python 2.6, pero está no en desuso en Python 3.x, ya que en 3.x se implementa utilizandosubprocess.Popen()
.Su millaje puede variar, intenté el giro de @ senderle en la solución de Vartec en Windows en Python 2.6.5, pero estaba recibiendo errores, y ninguna otra solución funcionó. Mi error fue:
WindowsError: [Error 6] The handle is invalid
.Descubrí que tenía que asignar PIPE a cada identificador para que devolviera el resultado que esperaba: lo siguiente funcionó para mí.
y llame así, (
[0]
obtiene el primer elemento de la tuplastdout
):Después de aprender más, creo que necesito estos argumentos de tubería porque estoy trabajando en un sistema personalizado que utiliza diferentes controladores, por lo que tuve que controlar directamente todos los estándares.
Para detener las ventanas emergentes de la consola (con Windows), haga esto:
fuente
DEVNULL
lugar desubprocess.PIPE
si no escribe / lee desde una tubería; de lo contrario, puede colgar el proceso secundario.Tenía un sabor ligeramente diferente del mismo problema con los siguientes requisitos:
palabra clave "rendimiento" arriba
He combinado y ajustado las respuestas anteriores para llegar a lo siguiente:
Este código se ejecutará igual que las respuestas anteriores:
fuente
p.poll()
sin dormir entre llamadas, desperdiciaríamos los ciclos de la CPU llamando a esta función millones de veces. En cambio, "aceleramos" nuestro ciclo diciéndole al sistema operativo que no necesitamos que nos molesten durante el próximo 1/10 de segundo, para que pueda llevar a cabo otras tareas. (Es posible que p.poll () también duerma, haciendo que nuestra declaración de suspensión sea redundante).Dividir el comando inicial para el
subprocess
podría ser complicado y engorroso.Úselo
shlex.split()
para ayudarse.Comando de muestra
git log -n 5 --since "5 years ago" --until "2 year ago"
El código
Sin
shlex.split()
el código se vería de la siguiente manerafuente
shlex.split()
es una conveniencia, especialmente si no sabe cómo funciona exactamente la cita en el shell; pero la conversión manual de esta cadena a la lista['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']
no es nada difícil si comprende las citas.Si necesita ejecutar un comando de shell en varios archivos, esto me sirvió.
Editar: Acabo de ver la solución de Max Persson con la sugerencia de JF Sebastian. Se adelantó e incorporó eso.
fuente
Popen
acepta una cadena, pero luego la necesitashell=True
, o una lista de argumentos, en cuyo caso debe pasar en['nm', filename]
lugar de una cadena. Lo último es preferible porque el shell agrega complejidad sin proporcionar ningún valor aquí. Pasar una cadena sinshell=True
aparentemente funciona en Windows, pero eso podría cambiar en cualquier próxima versión de Python.Según @senderle, si usas python3.6 como yo:
Actuará exactamente como si ejecutaras el comando en bash
fuente
check=True, universal_newlines=True
. En otras palabras,subprocess.run()
ya hace todo lo que hace su código.Si utiliza el
subprocess
módulo python, puede manejar el STDOUT, STDERR y el código de comando de retorno por separado. Puede ver un ejemplo para la implementación completa del llamador de comandos. Por supuesto, puede ampliarlotry..except
si lo desea.La siguiente función devuelve el código STDOUT, STDERR y Return para que pueda manejarlos en el otro script.
fuente
subprocess.run()
. No reinventes la rueda.por ejemplo, execute ('ls -ahl') diferenciaba tres / cuatro posibles retornos y plataformas de SO:
funcionar a continuación
fuente
El resultado se puede redirigir a un archivo de texto y luego volver a leerlo.
fuente
stdout=temp_file
?ecls.exe
parece que implementa otra herramienta de línea de comandos, por lo que la forma simple a veces no funcionó.acabo de escribir un pequeño script bash para hacer esto usando curl
https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5
fuente