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_outputfunción:check_outputejecuta 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 seccionesrunoPopen. Si desea ejecutar comandos de shell complejos, vea la notashell=Trueal final de esta respuesta.La
check_outputfunció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):
runSi está utilizando Python 3.5 o superior, y no necesita compatibilidad con versiones anteriores , se recomienda la nueva
runfunción . Proporciona una API muy general de alto nivel para elsubprocessmódulo. Para capturar la salida de un programa, pase lasubprocess.PIPEbandera alstdoutargumento de la palabra clave. Luego acceda alstdoutatributo delCompletedProcessobjeto devuelto :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:Todo esto se puede comprimir en una sola línea:
Si desea pasar la entrada al proceso
stdin, pase unbytesobjeto alinputargumento de la palabra clave:Puede capturar errores pasando
stderr=subprocess.PIPE(capturar aresult.stderr) ostderr=subprocess.STDOUT(capturarresult.stdoutjunto con la salida regular). Cuando la seguridad no es una preocupación, también puede ejecutar comandos de shell más complejos pasandoshell=Truecomo 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
runfunción.Versiones anteriores de Python (2.7-3.4):
check_outputSi está utilizando una versión anterior de Python, o necesita una modesta compatibilidad con versiones anteriores, probablemente pueda usar la
check_outputfunció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_outputes equivalente a ejecutarrunconcheck=Trueystdout=PIPE, y devolver solo elstdoutatributo.Puede pasar
stderr=subprocess.STDOUTa garantizar que los mensajes de error se incluyen en la salida devuelta - pero en algunas versiones de Python que pasastderr=subprocess.PIPEacheck_outputpuede causar bloqueos . Cuando la seguridad no es una preocupación, también puede ejecutar comandos de shell más complejos pasandoshell=Truecomo se describe en las notas a continuación.Si necesita canalizar
stderro pasar la entrada al proceso,check_outputno estará a la altura de la tarea. Vea losPopenejemplos a continuación en ese caso.Aplicaciones complejas y versiones heredadas de Python (2.6 y siguientes):
PopenSi necesita una profunda compatibilidad con versiones anteriores, o si necesita una funcionalidad más sofisticada que la que
check_outputproporciona, tendrá que trabajar directamente conPopenobjetos, que encapsulan la API de bajo nivel para subprocesos.El
Popenconstructor 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.splitpuede ayudar a analizar cadenas en listas con el formato adecuado.PopenLos 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,
communicatecasi siempre es el método preferido. Como en:O
Si configura
stdin=PIPE,communicatetambié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,stderrystdinque todosPIPE(oDEVNULL) para llegarcommunicateal 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
communicateson 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=TrueargumentoNormalmente, cada llamada a
run,check_outputo elPopenconstructor 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_outputdevuelve una cadena en Python 2, pero unbytesobjeto 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.outfue 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.splitcuando pasasshell=True.shlex.splites para comandos que no son de shell. Creo que voy a sacar esa parte porque esto está enturbiando las aguas.universal_newlines=Trueque 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.encodingparámetro de ensubprocess.runlugar 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
subprocessmó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
waity lascallfunciones.PIPEvalorstdiny cerrar ese archivo tan pronto comoPopenregrese.retcodees0. 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(); breakLa 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
mktemppara 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
DEVNULLlugar desubprocess.PIPEsi 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
subprocesspodrí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
Popenacepta 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=Trueaparentemente 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
subprocessmó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..exceptsi 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.exeparece 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