Llamar a un comando externo desde Python
¿Cómo se llama a un comando externo (como si lo hubiera escrito en el shell de Unix o en el símbolo del sistema de Windows) desde un script de Python?
Mostrar la mejor respuesta¿Cómo se llama a un comando externo (como si lo hubiera escrito en el shell de Unix o en el símbolo del sistema de Windows) desde un script de Python?
Mostrar la mejor respuestaAntes de publicar una nueva respuesta, considere que ya hay más de 60 respuestas para esta pregunta. Por favor, asegúrese de que su respuesta contribuya con información que no se encuentre entre las respuestas existentes.
Antes de usar el subproceso u os.system, considere buscar un paquete de Python que haga lo mismo. Por lo general, hay envoltorios sobre utilidades bash comúnmente utilizadas que puede aprovechar.
import os
os.system("your command")
Tenga en cuenta que esto es peligroso, ya que el comando no se limpia. Te dejo a Google la documentación relevante sobre los módulos 'os' y 'sys'. Hay un montón de funciones (exec * y spawn *) que harán cosas similares.
No tengo idea de lo que quise decir hace casi una década (¡compruebe la fecha!), Pero si tuviera que adivinar, sería que no se realiza ninguna validación.
Ahora debería apuntar a subprocess
como una solución ligeramente más versátil y portátil. Por supuesto, ejecutar comandos externos es inherentemente inportable (debe asegurarse de que el comando esté disponible en todas las arquitecturas que necesita admitir) y pasar la entrada del usuario como un comando externo es inherentemente inseguro.
Tenga en cuenta la marca de tiempo en este tipo: la respuesta "correcta" tiene 40 veces los votos y es la respuesta # 1.
La única solución que funcionó para mí para ejecutar cosas de NodeJS.
import os
cmd = 'ls -al'
os.system(cmd)
Si desea devolver los resultados del comando, puede usar os.popen
. Sin embargo, esto está en desuso desde la versión 2.6 a favor del módulo de subproceso , que otras respuestas han cubierto bien.
popen está en desuso a favor de subproceso .
También puede guardar su resultado con la llamada os.system, ya que funciona como el shell de UNIX, como por ejemplo os.system ('ls -l> test2.txt')
Mire el módulo de subproceso en la biblioteca estándar:
import subprocess
subprocess.run(["ls", "-l"])
La ventaja de subprocess
vs. system
es que es más flexible (puede obtener stdout
, stderr
, el código de estado "real", mejor manejo de errores, etc.).
La documentación oficial recomienda el subprocess
módulo sobre la alternativa os.system()
:
El módulo
subprocess
proporciona instalaciones más potentes para generar nuevos procesos y recuperar sus resultados; es preferible usar ese módulo que usar esta función [os.system()
].
El Reemplazar funciones más antiguas con La sección del módulo de subproceso en la documentación de subprocess
puede tener algunas recetas útiles.
Para versiones de Python anteriores a 3.5, use call
:
import subprocess
subprocess.call(["ls", "-l"])
¿Hay alguna manera de usar la sustitución de variables? IE Intenté hacer echo $PATH
usando call(["echo", "$PATH"])
, pero solo hizo eco de la cadena literal $PATH
en lugar de hacer cualquier sustitución. Sé que podría obtener la variable de entorno PATH, pero me pregunto si hay una manera fácil de hacer que el comando se comporte exactamente como si lo hubiera ejecutado en bash.
@KevinWheeler Tendrás que usar shell=True
para que eso funcione.
@KevinWheeler NO debe usar shell=True
, para este propósito Python viene con os.path.expandvars . En su caso, puede escribir: os.path.expandvars("$PATH")
. @SethMMorton reconsidere su comentario -> ¿Por qué no utilizar shell = Cierto
El ejemplo llama a ls -l
pero no da acceso a su salida (stdout
no es accesible). Me parece confuso: en su lugar, podría usar un comando sin stdout, como touch
.
Parece ser bastante torpe en Python3 + Windows
. Si ingreso un nombre de archivo con caracteres especiales como &
, arrojará un FileNotFoundError
. Aunque el archivo está en el directorio de trabajo donde ejecuté python, y obviamente existe.
¿bloquea la llamada? es decir, si quiero ejecutar varios comandos en un bucle for
, ¿cómo lo hago sin bloquear mi script de Python? No me importa la salida del comando, solo quiero ejecutar muchos de ellos.
Entiendo el uso de call
para funciones más avanzadas, pero no veo nada de malo en usar system
si hace lo que necesita.
Para simplificar al menos conceptualmente: \ n call ("ls -l" .split ())
Si desea crear una lista a partir de un comando con parámetros , una lista que puede usarse con subprocess
cuando shell=False
, luego use shlex.split code> para una manera fácil de hacer esto docs.python.org/ 2 / library / shlex.html # shlex.split
subprocess
también le permite conectar directamente dos comandos, hay un ejemplo en los documentos.
@CharlieParker: call
, check_call
, check_output
y run
bloques. Si desea no bloquear, use subprocess.Popen
.
@sudo: lo tienes al revés. system()
es más avanzado que Popen
, agrega un maldito shell que está listo para fastidiarte cuando menos lo esperas. Cual caparazón? Depende de lo que el usuario tenga como SHELL env, lo que significa que la sintaxis exacta que te atornillará a menudo está fuera de tu control. Popen es de nivel inferior y está más cerca de cómo se ve la API del sistema operativo subyacente, y tiene un comportamiento mucho menos sorprendente.
@LieRyan Usaría Popen
si la sintaxis exacta fuera algo de lo que preocuparse, pero si solo está ejecutando algún programa (tal vez con un par de argumentos constantes), system
no un problema. Además, a veces quiero el caparazón.
olvidó decir que necesita Python 3.5 al menos. No funciona en Python 3.4.3, por ejemplo, que es el predeterminado para Ubuntu 14.04 LTS
Como ya se mencionó, esto solo se admite en versiones recientes de python. Creo que esto debería indicarse en la respuesta
La respuesta original tenía subprocess.call()
, por lo que la persona que actualizó la respuesta (¡no el respondedor original!) También debería explicar este cambio con más detalle.
También puede pasar como cadena en lugar de una lista de cadenas: subprocess.run("ls -l")
Si quieren nombrarlo run
en lugar de call
, ¿por qué no simplemente convertirlo en un alias? Algunos tipos de compatibilidad con versiones anteriores son difíciles y permiten que continúen los patrones propensos a errores, pero hacer run == call
es fácil, simple y casi sin costo. ¿Por qué call
para versiones de Python anteriores a 3.5 en lugar de versiones nuevas y antiguas?
@SamuelMuldoon El problema es que subprocess.call
tiene una semántica completamente diferente de subprocess.run
. subprocess.call
todavía existe en Python 3.5–3.8 (para compatibilidad con versiones anteriores), es solo que si lo tiene disponible, subprocess.run
es mejor con diferencia.
También puede usar os.popen
. stackoverflow.com/a/59050139/9789097
Recomiendo usar el módulo de subproceso en lugar de os.system porque escapa de shell por usted y, por lo tanto, es mucho más seguro: http://docs.python.org/library/subprocess.html
subprocess.call(['ping', 'localhost'])
Si quiere crear una lista a partir de un comando con parámetros , una lista que se puede usar con subprocess
cuando shell=False
, luego use shlex.split code> para una manera fácil de hacer esto docs.python.org/ 2 / library / shlex.html # shlex.split (es la forma recomendada según los documentos docs.python.org/2/library/subprocess.html#popen-constructor )
Esto es incorrecto: " se escapa por usted y, por lo tanto, es mucho más seguro ". el subproceso no escapa del shell, el subproceso no pasa su comando a través del shell, por lo que no hay necesidad de escapar del shell.
os.system
está bien, pero está un poco anticuado. Tampoco es muy seguro. En su lugar, intente subprocess
. subprocess
no llama a sh directamente y, por lo tanto, es más seguro que os.system
.
Obtenga más información aquí .
Si bien estoy de acuerdo con la recomendación general, subprocess
no elimina todos los problemas de seguridad y tiene algunos problemas molestos.
Aquí hay un resumen de las formas de llamar a programas externos y las ventajas y desventajas de cada uno:
os.system("some_command with args")
pasa el comando y los argumentos al shell de su sistema. Esto es bueno porque en realidad puede ejecutar múltiples comandos a la vez de esta manera y configurar tuberías y redirección de entrada / salida. Por ejemplo:
os.system("some_command < input_file | another_command > output_file")
Sin embargo, si bien esto es conveniente, debe manejar manualmente el escape de caracteres de shell como espacios, etc. Por otro lado, esto también le permite ejecutar comandos que son simplemente comandos de shell y no programas externos. Consulte la documentación .
stream = os.popen("some_command with args")
hará lo mismo que os.system
excepto que le proporciona un objeto similar a un archivo que puede usar para acceder a la entrada / salida estándar para ese proceso. Hay otras 3 variantes de popen que manejan la E / S de forma ligeramente diferente. Si pasa todo como una cadena, su comando se pasa al shell; si los pasa como una lista, entonces no necesita preocuparse por escapar de nada. Consulte la documentación .
La clase Popen
del módulo subprocess
. Esto pretende ser un reemplazo de os.popen
pero tiene la desventaja de ser un poco más complicado en virtud de ser tan completo. Por ejemplo, diría:
print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
en lugar de:
print os.popen("echo Hello World").read()
pero es bueno tener todas las opciones allí en una clase unificada en lugar de 4 funciones de popen diferentes. Consulte la documentación .
La función call
del módulo subprocess
. Esto es básicamente como la clase Popen
y toma todos los mismos argumentos, pero simplemente espera hasta que el comando se complete y le dé el código de retorno. Por ejemplo:
return_code = subprocess.call("echo Hello World", shell=True)
Consulte la documentación .
Si está en Python 3.5 o posterior, puede usar el nuevo función subprocess.run
, que es muy parecida a la anterior pero aún más flexible y devuelve una objeto CompletedProcess
cuando el comando termina de ejecutarse.
El módulo os también tiene todas las funciones fork / exec / spawn que tendrías en un programa en C, pero no recomiendo usarlas directamente.
El módulo subprocess
probablemente debería ser lo que usa.
Finalmente, tenga en cuenta que para todos los métodos en los que pasa el comando final para que el shell lo ejecute como una cadena y usted es responsable de escapar de él. Existen serias implicaciones de seguridad si no se puede confiar completamente en alguna parte de la cadena que pasa. Por ejemplo, si un usuario está ingresando alguna / cualquier parte de la cadena. Si no está seguro, solo use estos métodos con constantes. Para darle una pista de las implicaciones, considere este código:
print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()
e imagine que el usuario ingresa algo "mi mamá no me amaba && rm -rf /" que podría borrar todo el sistema de archivos.
Buena respuesta / explicación. ¿Cómo justifica esta respuesta el lema de Python como se describe en este artículo? fastcompany.com/3026446/… a> "Estilísticamente, Perl y Python tienen filosofías diferentes. Los lemas más conocidos de Perl son" Hay más de una forma de hacerlo ". Python está diseñado para tener una forma obvia de hacerlo" ¡Parece que debería ser a la inversa! En Perl, conozco solo dos formas de ejecutar un comando: usar la tecla de retroceso o open
.
Si usa Python 3.5+, use subprocess.run()
. docs.python.org/3.5/library/subprocess.html#subprocess. correr
Lo que normalmente se necesita saber es qué se hace con STDOUT y STDERR del proceso hijo, porque si se ignoran, en algunas condiciones (bastante comunes), eventualmente el proceso hijo emitirá una llamada del sistema para escribir en STDOUT (¿STDERR también?) eso excedería el búfer de salida proporcionado por el sistema operativo para el proceso, y el sistema operativo hará que se bloquee hasta que algún proceso lea de ese búfer. Entonces, con las formas actualmente recomendadas, subprocess.run(..)
, ¿qué significa exactamente "Esto no captura stdout o stderr por defecto" ? ¿Qué pasa con subprocess.check_output(..)
y STDERR?
¿Cuál de los comandos que recomendó bloquea mi script? es decir, si quiero ejecutar varios comandos en un bucle for
, ¿cómo lo hago sin bloquear mi script de Python? No me importa la salida del comando, solo quiero ejecutar muchos de ellos.
@ Phoenix no estoy de acuerdo. No hay nada que le impida utilizar os.system en python3 docs.python. org / 3 / library / os.html # os.system
mi mamá no me amó && rm -rf / no hará nada: D Probablemente mi mamá no me amó || rm -rf / será más peligroso :)
@Pitto sí, pero eso no es lo que se ejecuta con el ejemplo. ¿Observa el echo
delante de la cadena pasada a Popen
? Entonces el comando completo será echo my mama didnt love me && rm -rf /
.
Esto es posiblemente el camino equivocado. La mayoría de las personas solo necesitan subprocess.run()
o sus hermanos mayores subprocess.check_call()
et al. Para los casos en que estos no son suficientes, consulte subprocess.Popen()
. os.popen()
quizás no debería mencionarse en absoluto, o incluso después de "piratear su propio código fork / exec / spawn".
Implementación típica:
import subprocess
p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
print line,
retval = p.wait()
Usted es libre de hacer lo que quiera con los datos stdout
en la tubería. De hecho, simplemente puede omitir esos parámetros (stdout=
y stderr=
) y se comportará como os.system()
.
.readlines()
lee todas líneas a la vez, es decir, bloquea hasta que sale el subproceso (cierra su extremo de la tubería). Para leer en tiempo real (si no hay problemas de almacenamiento en búfer) puede: for line in iter(p.stdout.readline, ''): print line,
¿Podría explicar qué quiere decir con "si no hay problemas de almacenamiento en búfer"? Si el proceso se bloquea definitivamente, la llamada del subproceso también se bloquea. Lo mismo podría suceder con mi ejemplo original también. ¿Qué más podría pasar con respecto al almacenamiento en búfer?
el proceso secundario puede usar el almacenamiento en bloque en modo no interactivo en lugar del almacenamiento en línea para que p.stdout.readline()
(nota: no s
al final) no vea ningún dato hasta que el niño llena su búfer Si el niño no produce muchos datos, la salida no será en tiempo real. Vea la segunda razón en P: ¿Por qué no usar una tubería? (popen ())? . Se proporcionan algunas soluciones en esta respuesta (pexpect, pty, stdbuf)
el problema del almacenamiento en búfer solo es importante si desea la salida en tiempo real y no se aplica a su código que no imprime nada hasta que se reciben todos datos
Esta respuesta estuvo bien para su tiempo, pero ya no deberíamos recomendar Popen
para tareas simples. Esto también especifica innecesariamente shell=True
. Pruebe una de las respuestas subprocess.run()
.
Hay otra diferencia aquí que no se menciona anteriormente.
subprocess.Popen
ejecuta el
Intenté con el subproceso y la ejecución fue exitosa. Sin embargo, no pudo comunicarse con . Todo es normal cuando ejecuto ambos desde la terminal.
Uno más: (NOTA: kwrite se comporta de manera diferente a otras aplicaciones. Si prueba lo siguiente con Firefox, los resultados no serán los mismos).
Si intenta os.system("kwrite")
, el flujo del programa se congela hasta que el usuario cierre kwrite. Para superar eso, intenté en su lugar os.system(konsole -e kwrite)
. Esta vez el programa continuó fluyendo, pero kwrite se convirtió en el subproceso de la consola.
Cualquiera ejecuta kwrite no siendo un subproceso (es decir, en el monitor del sistema debe aparecer en el extremo izquierdo del árbol).
¿Qué quiere decir con "Alguien ejecuta kwrite sin ser un subproceso" ?
Algunas sugerencias para separar el proceso hijo del que llama (iniciar el proceso hijo en segundo plano).
Suponga que desea comenzar una tarea larga desde un script CGI. Es decir, el proceso secundario debería vivir más tiempo que el proceso de ejecución del script CGI.
El ejemplo clásico de la documentación del módulo de subproceso es:
import subprocess
import sys
# Some code here
pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess
# Some more code here
La idea aquí es que no desea esperar en la línea 'llamar subproceso' hasta que finalice longtask.py. Pero no está claro qué sucede después de la línea 'más código aquí' del ejemplo.
Mi plataforma de destino era FreeBSD, pero el desarrollo estaba en Windows, así que enfrenté el problema en Windows primero.
En Windows (Windows XP), el proceso padre no finalizará hasta que longtask.py haya finalizado su trabajo. No es lo que quieres en un script CGI. El problema no es específico de Python; en la comunidad PHP los problemas son los mismos.
La solución es pasar DETACHED_PROCESS Indicador de creación de proceso a la función CreateProcess subyacente en la API de Windows. Si tiene instalado pywin32, puede importar el indicador desde el módulo de proceso win32, de lo contrario, debe definirlo usted mismo:
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen([sys.executable, "longtask.py"],
creationflags=DETACHED_PROCESS).pid
/ * UPD 2015.10.27 @eryksun en un comentario debajo de las notas, que el indicador semánticamente correcto es CREATE_NEW_CONSOLE (0x00000010) * /
En FreeBSD tenemos otro problema: cuando el proceso padre finaliza, también finaliza el proceso hijo. Y eso tampoco es lo que quieres en un script CGI. Algunos experimentos mostraron que el problema parecía estar en compartir sys.stdout. Y la solución de trabajo fue la siguiente:
pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
No he verificado el código en otras plataformas y no sé las razones del comportamiento en FreeBSD. Si alguien lo sabe, por favor comparta sus ideas. Buscar en Google al iniciar procesos en segundo plano en Python todavía no arroja ninguna luz.
Noté un posible "capricho" con el desarrollo de aplicaciones py2exe en pydev + eclipse. pude decir que el script principal no estaba separado porque la ventana de salida de eclipse no estaba terminando; incluso si el script se ejecuta hasta su finalización, todavía está esperando devoluciones. pero, cuando intenté compilar en un ejecutable py2exe, ocurre el comportamiento esperado (ejecuta los procesos como separados, luego se cierra). No estoy seguro, pero el nombre del ejecutable ya no está en la lista de procesos. esto funciona para todos los enfoques (sistema os. ("inicio *"), os.spawnl con os.P_DETACH, subprocs, etc.)
Problema de Windows: aunque generé el proceso con DETACHED_PROCESS, cuando eliminé mi demonio Python, todos los puertos abiertos por él no se liberarían hasta que finalicen todos los procesos generados. WScript.Shell resolvió todos mis problemas. Ejemplo aquí: pastebin.com/xGmuvwSx
También es posible que necesites el indicador CREATE_NEW_PROCESS_GROUP. Consulte Popen esperando el proceso hijo incluso cuando el hijo inmediato ha finalizado
Estoy viendo que import subprocess as sp;sp.Popen('calc')
no está esperando que se complete el subproceso. Parece que las banderas de creación no son necesarias. ¿Qué me estoy perdiendo?
@ubershmekel, no estoy seguro de lo que quieres decir y no tengo una instalación de Windows. Si recuerdo correctamente, sin los indicadores no puede cerrar la instancia de cmd
desde la que inició el calc
.
Estoy en Windows 8.1 y calc
parece sobrevivir al cierre de python
.
¿Hay algún significado para usar '0x00000008'? ¿Es ese un valor específico que debe usarse o una de las múltiples opciones?
Lo siguiente es incorrecto: "[o] n windows (win xp), el proceso padre no finalizará hasta que longtask.py haya terminado su trabajo". El padre saldrá normalmente, pero la ventana de la consola (instancia de conhost.exe) solo se cierra cuando sale el último proceso adjunto, y el hijo puede haber heredado la consola del padre. Establecer DETACHED_PROCESS
en creationflags
evita esto al evitar que el niño herede o cree una consola. Si en cambio desea una nueva consola, use CREATE_NEW_CONSOLE
(0x00000010).
No quise decir que ejecutar como un proceso separado es incorrecto. Dicho esto, es posible que deba configurar los identificadores estándar para archivos, canalizaciones o os.devnull
porque, de lo contrario, algunos programas de consola salen con un error. Cree una nueva consola cuando desee que el proceso secundario interactúe con el usuario simultáneamente con el proceso primario. Sería confuso intentar hacer ambas cosas en una sola ventana.
stdout=subprocess.PIPE
hará que su código cuelgue si tiene una salida larga de un niño. Para obtener más detalles, consulte thraxil .org / users / anders / posts / 2008/03/13 / ...
¿No hay una manera independiente del sistema operativo para ejecutar el proceso en segundo plano?
tu respuesta me parece extraña Acabo de abrir un subprocess.Popen
y no pasó nada malo (no tuve que esperar). ¿Por qué exactamente debemos preocuparnos por el escenario que está señalando? Soy escéptico
Compruebe también la biblioteca Python "pexpect".
Permite el control interactivo de programas / comandos externos, incluso ssh, ftp, telnet, etc. Puede escribir algo como:
child = pexpect.spawn('ftp 192.168.0.24')
child.expect('(?i)name .*: ')
child.sendline('anonymous')
child.expect('(?i)password')
subprocess.check_call
es conveniente si no desea probar los valores de retorno. Lanza una excepción en cualquier error.
Si necesita la salida del comando que está llamando, entonces puede usar subprocess.check_output (Python 2.7+).
>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
También tenga en cuenta el parámetro shell .
Si el shell es
True
, el comando especificado se ejecutará a través del shell. Esto puede ser útil si está utilizando Python principalmente para el flujo de control mejorado que ofrece sobre la mayoría de los shells del sistema y aún desea un acceso conveniente a otras características de shell, como tuberías de shell, comodines de nombre de archivo, expansión de variable de entorno y expansión de ~ a la página de inicio de un usuario directorio. Sin embargo, tenga en cuenta que Python ofrece implementaciones de muchas características similares a las de shell (en particular,glob
,fnmatch
,os.walk()
,os.path.expandvars()
,os.path.expanduser()
yshutil
).
Tenga en cuenta que check_output
requiere una lista en lugar de una cadena. Si no confía en los espacios entre comillas para que su llamada sea válida, la forma más simple y legible de hacerlo es subprocess.check_output("ls -l /dev/null".split())
.
Siempre uso fabric
para estas cosas como:
from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )
Pero esta parece ser una buena herramienta: sh
(interfaz de subproceso de Python) .
Mira un ejemplo:
from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)
os.system
no le permite almacenar resultados, por lo que si desea almacenar los resultados en alguna lista o algo, un subprocess.call
funciona.
Puede usar Popen, y luego puede verificar el estado del procedimiento:
from subprocess import Popen
proc = Popen(['ls', '-l'])
if proc.poll() is None:
proc.kill()
Consulte subprocess.Popen .
La forma más sencilla de ejecutar cualquier comando y recuperar el resultado:
from commands import getstatusoutput
try:
return getstatusoutput("ls -ltr")
except Exception, e:
return None
De hecho, la commands
documentación de Python 2.7 lo dice quedó en desuso en 2.6 y se eliminará en 3.0.
Me gusta bastante shell_command por su simplicidad. Está construido sobre el módulo de subproceso.
Aquí hay un ejemplo de la documentación:
>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py shell_command.py test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan 391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0
Así es como ejecuto mis comandos. Este código tiene todo lo que necesitas prácticamente
from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()
Creo que es aceptable para los comandos codificados, si aumenta la legibilidad.
subprocess.run
es el enfoque recomendado a partir de Python 3.5 si su código no necesita mantener la compatibilidad con versiones anteriores de Python. Es más consistente y ofrece una facilidad de uso similar a Envoy. (Sin embargo, las tuberías no son tan sencillas. Consulte esta pregunta sobre cómo .)
Aquí hay algunos ejemplos de la documentación .
Ejecute un proceso:
>>> subprocess.run(["ls", "-l"]) # Doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
Aumento en ejecución fallida:
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
Salida de captura:
>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
Recomiendo probar Envoy . Es un contenedor para subprocesos, que a su vez tiene como objetivo reemplazar los módulos más antiguos y funciones Enviado es un subproceso para los humanos.
Ejemplo de uso de the README :
>>> r = envoy.run('git config', data='data to pipe in', timeout=2)
>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''
Tubería alrededor también:
>>> r = envoy.run('uptime | pbcopy')
>>> r.command
'pbcopy'
>>> r.status_code
0
>>> r.history
[<Response 'uptime'>]
Utilice el módulo de subproceso (Python 3):
import subprocess
subprocess.run(['ls', '-l'])
Es la forma estándar recomendada. Sin embargo, las tareas más complicadas (canalizaciones, salida, entrada, etc.) pueden ser tediosas de construir y escribir.
Nota sobre la versión de Python: si todavía usa Python 2, subprocess.call funciona de manera similar.
ProTip: shlex.split puede ayudarlo a analizar el comando para run
, call
y otras funciones subprocess
en caso de que no desee (o no pueda) proporcionarlas en forma de listas:
import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))
Si no le importan las dependencias externas, use plumbum :
from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())
Es el mejor contenedor subprocess
. Es multiplataforma, es decir, funciona en sistemas Windows y Unix. Instalar por pip install plumbum
.
Otra biblioteca popular es sh :
from sh import ifconfig
print(ifconfig('wlan0'))
Sin embargo, sh
dejó de admitir Windows, por lo que ya no es tan impresionante como solía ser. Instalar por pip install sh
.
Hay muchas formas diferentes de ejecutar comandos externos en Python, y todos ellos tienen sus propias ventajas e inconvenientes.
Mis colegas y yo hemos estado escribiendo herramientas de administración del sistema Python, por lo que necesitamos ejecutar muchos comandos externos y, a veces, desea que bloqueen o se ejecuten de forma asincrónica, tiempo de espera, actualización cada segundo, etc.
También hay diferentes formas de manejar el código de retorno y los errores, y es posible que desee analizar la salida y proporcionar una nueva entrada (en un esperar tipo de estilo ) O deberá redirigir stdin, stdout y stderr para que se ejecuten en un tty diferente (por ejemplo, cuando se usa la pantalla).
Entonces probablemente tendrá que escribir muchos contenedores alrededor del comando externo. Así que aquí hay un módulo de Python que hemos escrito que puede manejar casi cualquier cosa que desee, y si no, es muy flexible para que pueda extenderlo fácilmente:
https://github.com /hpcugent/vsc-base/blob/master/lib/vsc/utils/run.py
actualización: no funciona de manera independiente y requiere algunas de nuestras otras herramientas, y obtuvo una gran cantidad de funcionalidades especializadas a lo largo de los años, por lo que puede que no sea una caída de reemplazo para usted, pero puede brindarle mucha información sobre cómo funcionan las partes internas de Python para ejecutar comandos y las ideas sobre cómo manejar ciertas situaciones.
Sin la salida del resultado:
import os
os.system("your command here")
Con salida del resultado:
import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")
commands
ya no está disponible en Python 3. Debería preferir subprocess
sobre os.system()
El módulo de subproceso descrito anteriormente por Eli es muy poderoso, pero la sintaxis hacer una llamada de sistema estándar de pantano e inspeccionar su salida es innecesariamente prolijo.
La forma más fácil de hacer una llamada al sistema es con el módulo de comandos ( Solo Linux).
> import commands
> commands.getstatusoutput("grep matter alice-in-wonderland.txt")
(0, "'Then it doesn't matter which way you go,' said the Cat.")
El primer elemento de la tupla es el código de retorno del proceso. El segundo elemento es su salida estándar (y error estándar, combinado).
Los desarrolladores de Python han "desaprobado" el módulo de comandos, pero eso no significa que no debas usarlo. Solo que ya no lo están desarrollando, lo cual está bien, porque ya es perfecto (en su función pequeña pero importante).
En desuso no solo significa "ya no se desarrolla", sino también "no se recomienda usar esto". Las funciones obsoletas pueden romperse en cualquier momento, pueden eliminarse en cualquier momento o pueden ser peligrosas. Nunca debe usar esto en un código importante. La desaprobación es simplemente una mejor manera de eliminar una característica de inmediato, ya que les da a los programadores el tiempo para adaptar y reemplazar sus funciones desaprobadas.
Solo para probar mi punto: "Desaprobado desde la versión 2.6: el módulo de comandos se ha eliminado en Python 3. Utilice el módulo de subproceso en su lugar".
¡No es peligroso! Los desarrolladores de Python son cuidadosos solo para romper las características entre las versiones principales (es decir, entre 2.xy 3.x). He estado usando el módulo de comandos desde Python 2.4 de 2004. Funciona igual hoy en Python 2.7.
Con peligroso, no quise decir que se puede eliminar en cualquier momento (ese es un problema diferente), tampoco dije que es peligroso usar este módulo específico. Sin embargo, puede volverse peligroso si se descubre una vulnerabilidad de seguridad pero el módulo no se desarrolla ni se mantiene más. (No quiero decir que este módulo es o no vulnerable a problemas de seguridad, solo hablando de cosas obsoletas en general)
Solo para agregar a la discusión, si incluye el uso de una consola Python, puede llamar a comandos externos desde IPython . Mientras está en el indicador de IPython, puede llamar a los comandos de shell con el prefijo '!'. También puede combinar el código Python con el shell y asignar la salida de los scripts del shell a las variables de Python.
Por ejemplo:
In [9]: mylist = !ls
In [10]: mylist
Out[10]:
['file1',
'file2',
'file3',]
Después de investigar un poco, tengo el siguiente código que me funciona muy bien. Básicamente imprime stdout y stderr en tiempo real.
stdout_result = 1
stderr_result = 1
def stdout_thread(pipe):
global stdout_result
while True:
out = pipe.stdout.read(1)
stdout_result = pipe.poll()
if out == '' and stdout_result is not None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
def stderr_thread(pipe):
global stderr_result
while True:
err = pipe.stderr.read(1)
stderr_result = pipe.poll()
if err == '' and stderr_result is not None:
break
if err != '':
sys.stdout.write(err)
sys.stdout.flush()
def exec_command(command, cwd=None):
if cwd is not None:
print '[' + ' '.join(command) + '] in ' + cwd
else:
print '[' + ' '.join(command) + ']'
p = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
)
out_thread = threading.Thread(name='stdout_thread', target=stdout_thread, args=(p,))
err_thread = threading.Thread(name='stderr_thread', target=stderr_thread, args=(p,))
err_thread.start()
out_thread.start()
out_thread.join()
err_thread.join()
return stdout_result + stderr_result
su código puede perder datos cuando se cierra el subproceso mientras hay algunos datos almacenados en el búfer. Lea hasta EOF, consulte teed_call ()
Utilice subprocess.call :
from subprocess import call
# Using list
call(["echo", "Hello", "world"])
# Single string argument varies across platforms so better split it
call("echo Hello world".split(" "))
Tiendo a utilizar subproceso junto con shlex (para manejar el escape de cadenas entre comillas):
>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)
Plug descarado, escribí una biblioteca para esto: P https://github.com/houqp/shell.py
Es básicamente un contenedor para popen y shlex por ahora. También admite comandos de canalización para que pueda encadenar comandos más fácilmente en Python. Para que pueda hacer cosas como:
ex('echo hello shell.py') | "awk '{print $2}'"
Una forma sencilla es utilizar el módulo os :
import os
os.system('ls')
Alternativamente, también puede usar el módulo de subproceso:
import subprocess
subprocess.check_call('ls')
Si desea que el resultado se almacene en una variable, pruebe:
import subprocess
r = subprocess.check_output('ls')
También hay Plumbum
>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
>>> notepad = local["c:\\windows\\notepad.exe"]
>>> notepad() # Notepad window pops up
u'' # Notepad window is closed by user, command returns
Uso:
import os
cmd = 'ls -al'
os.system(cmd)
os: este módulo proporciona una forma portátil de utilizar la funcionalidad dependiente del sistema operativo.
Para las funciones más os
, aquí está la documentación.
También está en desuso. usar subproceso
Usar la función Popen
del módulo Python subprocess
es la forma más sencilla de ejecutar comandos de Linux. En eso, la función Popen.communicate()
le dará salida a sus comandos. Por ejemplo
import subprocess
..
process = subprocess.Popen(..) # Pass command and arguments to the function
stdout, stderr = process.communicate() # Get command output and error
..
Esto ya no es cierto, y probablemente no lo fue cuando se publicó esta respuesta. Debes preferir subprocess.check_call()
y amigos a menos que necesites absolutamente el control de nivel inferior del Popen()
más complejo. En versiones recientes de Python, el caballo de batalla es subprocess.run()
Aquí están mis dos centavos: en mi opinión, esta es la mejor práctica cuando se trata de comandos externos ...
Estos son los valores de retorno del método de ejecución ...
pass, stdout, stderr = execute(["ls","-la"],"/home/user/desktop")
Este es el método de ejecución ...
def execute(cmdArray,workingDir):
stdout = ''
stderr = ''
try:
try:
process = subprocess.Popen(cmdArray,cwd=workingDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1)
except OSError:
return [False, '', 'ERROR : command(' + ' '.join(cmdArray) + ') could not get executed!']
for line in iter(process.stdout.readline, b''):
try:
echoLine = line.decode("utf-8")
except:
echoLine = str(line)
stdout += echoLine
for line in iter(process.stderr.readline, b''):
try:
echoLine = line.decode("utf-8")
except:
echoLine = str(line)
stderr += echoLine
except (KeyboardInterrupt,SystemExit) as err:
return [False,'',str(err)]
process.stdout.close()
returnCode = process.wait()
if returnCode != 0 or stderr != '':
return [False, stdout, stderr]
else:
return [True, stdout, stderr]
Potencial de punto muerto: utilice el método .communicate
en su lugar
Mejor aún, evite Popen()
y use la API de nivel superior que ahora se recopila en la función única subprocess.run()
Para Python 3.5+ se recomienda que utilice la función de ejecución run desde el módulo de subproceso . Esto devuelve un objeto CompletedProcess
, desde el cual puede obtener fácilmente la salida y el código de retorno.
from subprocess import PIPE, run
command = ['echo', 'hello']
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(result.returncode, result.stdout, result.stderr)
La respuesta con la función de ejecución se agregó en 2015 años. Lo has repetido. Creo que fue un motivo de voto negativo
Recomendaría el siguiente método 'run' y nos ayudará a obtener STDOUT, STDERR y salir del estado como diccionario; La persona que llama puede leer el retorno del diccionario mediante el método 'ejecutar' para conocer el estado real del proceso.
def run (cmd):
print "+ DEBUG exec({0})".format(cmd)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True)
(out, err) = p.communicate()
ret = p.wait()
out = filter(None, out.split('\n'))
err = filter(None, err.split('\n'))
ret = True if ret == 0 else False
return dict({'output': out, 'error': err, 'status': ret})
#end
Esto reimplementa de manera incompleta algo como subprocess.run()
. En particular, debe evitar shell=True
cuando no sea estrictamente necesario.
En Windows puede importar el módulo subprocess
y ejecutar comandos externos llamando a subprocess.Popen()
, subprocess.Popen().communicate()
y subprocess.Popen().wait()
de la siguiente manera:
# Python script to run a command line
import subprocess
def execute(cmd):
"""
Purpose : To execute a command and return exit status
Argument : cmd - command to execute
Return : exit_code
"""
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait()
if rc != 0:
print "Error: failed to execute command:", cmd
print error
return result
# def
command = "tasklist | grep python"
print "This process detail: \n", execute(command)
Salida:
This process detail:
python.exe 604 RDP-Tcp#0 4 5,660 K
Uso:
import subprocess
p = subprocess.Popen("df -h", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")
Da una salida agradable con la que es más fácil trabajar:
['Filesystem Size Used Avail Use% Mounted on',
'/dev/sda6 32G 21G 11G 67% /',
'none 4.0K 0 4.0K 0% /sys/fs/cgroup',
'udev 1.9G 4.0K 1.9G 1% /dev',
'tmpfs 387M 1.4M 386M 1% /run',
'none 5.0M 0 5.0M 0% /run/lock',
'none 1.9G 58M 1.9G 3% /run/shm',
'none 100M 32K 100M 1% /run/user',
'/dev/sda5 340G 222G 100G 69% /home',
'']
Para obtener la identificación de red de OpenStack Neutron :
#!/usr/bin/python
import os
netid = "nova net-list | awk '/ External / { print $2 }'"
temp = os.popen(netid).read() /* Here temp also contains new line (\n) */
networkId = temp.rstrip()
print(networkId)
Salida de nova net-list
+--------------------------------------+------------+------+
| ID | Label | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1 | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal | None |
+--------------------------------------+------------+------+
Salida de print(networkId)
27a74fcd-37c0-4789-9414-9531b7e3f126
No debe recomendar os.popen()
en 2016. El script Awk podría reemplazarse fácilmente con código nativo de Python.
Hay muchas formas de llamar a un comando.
si and.exe
necesita dos parámetros. En cmd podemos llamar a sample.exe
use esto:
and.exe 2 3
y muestra 5
en la pantalla.
Si usamos un script de Python para llamar a and.exe
, deberíamos hacer como ...
os.system(cmd,...)
os.system(("and.exe" + " " + "2" + " " + "3"))
os.popen(cmd,...)
os.popen(("and.exe" + " " + "2" + " " + "3"))
subprocess.Popen(cmd,...)
subprocess.Popen(("and.exe" + " " + "2" + " " + "3"))
Es demasiado difícil, por lo que podemos unir cmd con un espacio:
import os
cmd = " ".join(exename,parameters)
os.popen(cmd)
os.popen
no debe recomendarse y quizás incluso mencionarse más. El ejemplo subpocess
debe pasar los argumentos como una lista en lugar de unirlos con espacios.
Aquí se llama a un comando externo y se devuelve o imprime la salida del comando:
Python Subproceso check_output es bueno para
Ejecute el comando con argumentos y devuelva su salida como una cadena de bytes.
import subprocess
proc = subprocess.check_output('ipconfig /all')
print proc
El argumento debe estar correctamente tokenizado en una lista, o debe pasar explícitamente en shell=True
. En Python 3.x (donde creo que x> 3) puede recuperar la salida como una cadena adecuada con universal_newlines=True
y probablemente desee cambiar a subproces.run()
En Linux, en caso de que desee llamar a un comando externo que se ejecutará de forma independiente (seguirá ejecutándose después de que finalice el script de Python), puede usar una cola simple como cola de tareas o el en comando
Un ejemplo con la cola de tareas:
import os
os.system('ts <your-command>')
Notas sobre la cola de tareas (ts
):
Puede establecer el número de procesos concurrentes que se ejecutarán ("slots") con:
ts -S <number-of-slots>
La instalación de ts
no requiere privilegios de administrador. Puede descargarlo y compilarlo desde la fuente con un simple make
, agregarlo a su ruta y listo.
ts
no es estándar en ninguna distribución que conozco, aunque el puntero a at
es ligeramente útil. Probablemente también deberías mencionar batch
. Como en otros lugares, la recomendación os.system()
probablemente debería mencionar al menos que subprocess
es su reemplazo recomendado.
Puede encontrar un ejemplo simple de comunicación bidireccional entre un proceso primario y un subproceso aquí: stackoverflow.com/a/52841475/1349673 a>
El primer ejemplo probablemente debería tener shell=True
o (mejor aún) pasar el comando como una lista.
He escrito un contenedor para manejar errores y redirigir la salida y otras cosas.
import shlex
import psutil
import subprocess
def call_cmd(cmd, stdout=sys.stdout, quiet=False, shell=False, raise_exceptions=True, use_shlex=True, timeout=None):
"""Exec command by command line like 'ln -ls "/var/log"'
"""
if not quiet:
print("Run %s", str(cmd))
if use_shlex and isinstance(cmd, (str, unicode)):
cmd = shlex.split(cmd)
if timeout is None:
process = subprocess.Popen(cmd, stdout=stdout, stderr=sys.stderr, shell=shell)
retcode = process.wait()
else:
process = subprocess.Popen(cmd, stdout=stdout, stderr=sys.stderr, shell=shell)
p = psutil.Process(process.pid)
finish, alive = psutil.wait_procs([p], timeout)
if len(alive) > 0:
ps = p.children()
ps.insert(0, p)
print('waiting for timeout again due to child process check')
finish, alive = psutil.wait_procs(ps, 0)
if len(alive) > 0:
print('process {} will be killed'.format([p.pid for p in alive]))
for p in alive:
p.kill()
if raise_exceptions:
print('External program timeout at {} {}'.format(timeout, cmd))
raise CalledProcessTimeout(1, cmd)
retcode = process.wait()
if retcode and raise_exceptions:
print("External program failed %s", str(cmd))
raise subprocess.CalledProcessError(retcode, cmd)
Puedes llamarlo así:
cmd = 'ln -ls "/var/log"'
stdout = 'out.txt'
call_cmd(cmd, stdout)
Como ejemplo (en Linux):
import subprocess
subprocess.run('mkdir test.dir', shell=True)
Esto crea test.dir en el directorio actual. Tenga en cuenta que esto también funciona:
import subprocess
subprocess.call('mkdir test.dir', shell=True)
El código equivalente que usa os.system es:
import os
os.system('mkdir test.dir')
La mejor práctica sería utilizar subprocesos en lugar de os, con .run como favorito sobre .call. Todo lo que necesita saber sobre el subproceso es aquí . Además, tenga en cuenta que toda la documentación de Python se puede descargar de aquí . Descargué el PDF empaquetado como .zip. Menciono esto porque hay una buena descripción del módulo os en tutorial.pdf (página 81). Además, es un recurso autorizado para los codificadores de Python.
@Nick Predley: anotado, pero "shell = False" no realiza la función deseada. ¿Cuáles son específicamente las preocupaciones de seguridad y cuál es la alternativa? Avíseme lo antes posible: no deseo publicar nada que pueda causar problemas a cualquiera que vea esto.
La advertencia básica se encuentra en la documentación, pero esta pregunta lo explica con más detalle: stackoverflow.com/questions/3172470/…
A menudo, uso la siguiente función para comandos externos, y esto es especialmente útil para procesos de larga ejecución . El siguiente método sigue la salida del proceso mientras se está ejecutando y devuelve la salida, genera una excepción si el proceso falla.
Sale si el proceso se realiza utilizando el método poll () en el proceso .
import subprocess,sys
def exec_long_running_proc(command, args):
cmd = "{} {}".format(command, " ".join(str(arg) if ' ' not in arg else arg.replace(' ','\ ') for arg in args))
print(cmd)
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Poll process for new output until finished
while True:
nextline = process.stdout.readline().decode('UTF-8')
if nextline == '' and process.poll() is not None:
break
sys.stdout.write(nextline)
sys.stdout.flush()
output = process.communicate()[0]
exitCode = process.returncode
if (exitCode == 0):
return output
else:
raise Exception(command, exitCode, output)
Puede invocarlo así:
exec_long_running_proc(command = "hive", args=["-f", hql_path])
Obtendrás resultados inesperados al pasar un argumento con espacio. Usar repr(arg)
en lugar de str(arg)
podría ayudar por la mera coincidencia de que python y sh escape se cita de la misma manera
@sbk repr(arg)
realmente no ayudó, el código anterior también maneja los espacios. Ahora lo siguiente funciona exec_long_running_proc(command = "ls", args=["-l", "~/test file*"])
Llamar a un comando externo en Python
Una forma sencilla de llamar a un comando externo es usar os.system(...)
. Y esta función devuelve el valor de salida del comando. Pero el inconveniente es que no obtendremos stdout y stderr.
ret = os.system('some_cmd.sh')
if ret != 0 :
print 'some_cmd.sh execution returned failure'
Llamar a un comando externo en Python en segundo plano
subprocess.Popen
proporciona más flexibilidad para ejecutar un comando externo en lugar de usar os.system
. Podemos iniciar un comando en segundo plano y esperar a que termine. Y después de eso podemos obtener stdout y stderr.
proc = subprocess.Popen(["./some_cmd.sh"], stdout=subprocess.PIPE)
print 'waiting for ' + str(proc.pid)
proc.wait()
print 'some_cmd.sh execution finished'
(out, err) = proc.communicate()
print 'some_cmd.sh output : ' + out
Llamar a un comando externo de larga ejecución en Python en segundo plano y detenerse después de un tiempo
Incluso podemos comenzar un proceso de larga ejecución en segundo plano usando subprocess.Popen
y matarlo después de algún tiempo una vez que se complete su tarea.
proc = subprocess.Popen(["./some_long_run_cmd.sh"], stdout=subprocess.PIPE)
# Do something else
# Now some_long_run_cmd.sh exeuction is no longer needed, so kill it
os.system('kill -15 ' + str(proc.pid))
print 'Output : ' proc.communicate()[0]
Puede ser así de simple:
import os
cmd = "your command"
os.system(cmd)
Esto no señala los inconvenientes, que se explican con mucho más detalle en PEP-324 . La documentación de os.system
recomienda explícitamente evitarlo a favor de subprocess
.
Si necesita llamar a un comando de shell desde un cuaderno de Python (como Jupyter , Zeppelin, Databricks o Google Cloud Datalab) solo puede usar el prefijo !
.
Por ejemplo,
!ls -ilF
Utilice el módulo os :
import os
os.system("your command")
Por ejemplo,
import os
os.system("ifconfig")
Esto duplica una respuesta (un poco) más detallada del abril anterior, que sin embargo tampoco señala las advertencias.
Invoke es una herramienta de ejecución de tareas Python (2.7 y 3.4+) y biblioteca. Proporciona una API limpia y de alto nivel para ejecutar comandos de shell
>>> from invoke import run
>>> cmd = "pip install -r requirements.txt"
>>> result = run(cmd, hide=True, warn=True)
>>> print(result.ok)
True
>>> print(result.stdout.splitlines()[-1])
Successfully installed invocations-0.13.0 pep8-1.5.7 spec-1.3.1
Esta es una gran biblioteca. Estaba tratando de explicárselo a un compañero de trabajo el otro día y lo describí así: invoke
es subprocess
como requests
es urllib3
.
Escribí una pequeña biblioteca para ayudar con este caso de uso:
https://pypi.org/project/citizenshell/
Se puede instalar usando
pip install citizenshell
Y luego se usa de la siguiente manera:
from citizenshell import sh
assert sh("echo Hello World") == "Hello World"
Puede separar stdout de stderr y extraer el código de salida de la siguiente manera:
result = sh(">&2 echo error && echo output && exit 13")
assert result.stdout() == ["output"]
assert result.stderr() == ["error"]
assert result.exit_code() == 13
Y lo bueno es que no tiene que esperar a que salga el shell subyacente antes de comenzar a procesar la salida:
for line in sh("for i in 1 2 3 4; do echo -n 'It is '; date +%H:%M:%S; sleep 1; done", wait=False)
print ">>>", line + "!"
imprimirá las líneas a medida que estén disponibles gracias a wait = False
>>> It is 14:24:52!
>>> It is 14:24:53!
>>> It is 14:24:54!
>>> It is 14:24:55!
Se pueden encontrar más ejemplos en https://github.com/meuter/citizenshell
Como algunas de las respuestas estaban relacionadas con las versiones anteriores de Python o estaban usando el módulo os.system
, publico esta respuesta para personas como yo que tienen la intención de usar subprocess
en Python 3.5 +. Lo siguiente me ayudó en Linux:
import subprocess
# subprocess.run() returns a completed process object that can be inspected
c = subprocess.run(["ls", "-ltrh"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(c.stdout.decode('utf-8'))
Como se menciona en la documentación , PIPE Los valores
son secuencias de bytes y, para mostrarlos correctamente, debe considerarse la decodificación. Para versiones posteriores de Python, text=True
y encoding='utf-8'
se agregan a kwargs de subprocess.run()
.
La salida del código mencionado anteriormente es:
total 113M
-rwxr-xr-x 1 farzad farzad 307 Jan 15 2018 vpnscript
-rwxrwxr-x 1 farzad farzad 204 Jan 15 2018 ex
drwxrwxr-x 4 farzad farzad 4.0K Jan 22 2018 scripts
.... # Some other lines
Hay dos formas destacadas en las que uno puede ejecutar comandos de shell utilizando Python. Los dos ejemplos mencionados a continuación muestran cómo se puede obtener el nombre del directorio de trabajo actual (pwd
) usando Python. Puede usar cualquier otro comando de Unix en lugar de pwd
.
1.> 1er método: se puede usar el os de Python, y system () a> funciona desde allí para ejecutar comandos de shell en Python.
import os
os.system('pwd')
Salida:
/Users/siddharth
1.> Segundo método: Otra forma es usar el módulo de subproceso y función call () .
import subprocess
subprocess.call('pwd')
Salida:
/Users/siddharth
Si no usa la entrada del usuario en los comandos, puede usar esto:
from os import getcwd
from subprocess import check_output
from shlex import quote
def sh(command):
return check_output(quote(command), shell=True, cwd=getcwd(), universal_newlines=True).strip()
Y úsalo como
branch = sh('git rev-parse --abbrev-ref HEAD')
shell=True
generará un shell, por lo que puede usar tubería y esas cosas sh('ps aux | grep python')
. Esto es muy muy útil para ejecutar comandos codificados y procesar su salida. El universal_lines=True
asegura que la salida se devuelva en una cadena en lugar de binaria.
cwd=getcwd()
se asegurará de que el comando se ejecute con el mismo directorio de trabajo que el intérprete. Esto es útil para que los comandos de Git funcionen como el ejemplo de nombre de rama de Git anterior.
Algunas recetas
sh('free -m').split('\n')[1].split()[1]
sh('df -m /').split('\n')[1].split()[4][0:-1]
sum(map(float, sh('ps -ef -o pcpu').split('\n')[1:])
Pero esto no es seguro para la entrada del usuario, de la documentación:
Consideraciones de seguridad
A diferencia de otras funciones de popen, esta implementación nunca llama implícitamente a un shell del sistema. Esto significa que todos los personajes, incluidos los metacaracteres de shell, se pueden pasar de forma segura al niño procesos. Si el shell se invoca explícitamente, a través de shell = True, es la responsabilidad de la aplicación de garantizar que todos los espacios en blanco y los metacaracteres se citan apropiadamente para evitar la inyección de shell vulnerabilidades.
Cuando se usa shell = True, la función shlex.quote () se puede usar para escapar adecuadamente los espacios en blanco y metacaracteres de shell en cadenas que se utilizarán para construir comandos de shell.
Incluso usando el shlex.quote()
, es bueno mantenerse un poco paranoico cuando se usan entradas de usuario en comandos de shell. Una opción es usar un comando codificado para tomar algunos resultados genéricos y filtrar por entrada del usuario. De todos modos, el uso de shell=False
se asegurará de que solo se ejecute el proceso exacto que desea ejecutar o obtendrá un error de No such file or directory
.
También hay un impacto en el rendimiento de shell=True
, de mis pruebas parece un 20% más lento que shell=False
(el valor predeterminado).
In [50]: timeit("check_output('ls -l'.split(), universal_newlines=True)", number=1000, globals=globals())
Out[50]: 2.6801227919995654
In [51]: timeit("check_output('ls -l', universal_newlines=True, shell=True)", number=1000, globals=globals())
Out[51]: 3.243950183999914
Sultan es un paquete reciente para este propósito. Proporciona algunas ventajas sobre la administración de privilegios de usuario y la adición de mensajes de error útiles.
from sultan.api import Sultan
with Sultan.load(sudo=True, hostname="myserver.com") as sultan:
sultan.yum("install -y tree").run()
Re "Proporciona algunas sutilezas" : ¿Puedes dar más detalles?
También puede usar subprocess.getoutput()
y subprocess.getstatusutput()
, que son Funciones de invocación de shell heredado del módulo commands
en desuso, es decir:
subprocess.getstatusoutput(cmd)
Retorno (
exitcode
,output
) de ejecutarcmd
en un shell.
subprocess.getoutput(cmd)
Devuelve la salida (
stdout
ystderr
) de ejecutarcmd
en un shell.
Si está escribiendo un script de shell Python y tiene IPython instalado en su sistema, puedes usar la magia de explosión para ejecutar un comando dentro de IPython:
!ls
filelist = !ls
@ PeterMortensen No creo que funcione en DOS, pero debería funcionar en Cygwin.
Python 3.5+
import subprocess
p = subprocess.run(["ls", "-ltr"], capture_output=True)
print(p.stdout.decode(), p.stderr.decode())
import subprocess
p = subprocess.run(["ls", "-ltr"], capture_output=True)
print(p.stdout.decode(), p.stderr.decode())
os.popen()
es la forma más fácil y segura de ejecutar un comando. Puede ejecutar cualquier comando que ejecute en la línea de comando. Además, también podrá capturar la salida del comando utilizando os.popen().read()
Puedes hacerlo así:
import os
output = os.popen('Your Command Here').read()
print (output)
Un ejemplo en el que enumera todos los archivos en el directorio actual:
import os
output = os.popen('ls').read()
print (output)
# Outputs list of files in the directory
LA MAYORÍA DE LOS CASOS:
Para la mayoría de los casos, un pequeño fragmento de código como este es todo lo que necesitará :
import subprocess
import shlex
source = "test.txt"
destination = "test_copy.txt"
base = "cp {source} {destination}'"
cmd = base.format(source=source, destination=destination)
subprocess.check_call(shlex.split(cmd))
Es limpio y simple .
subprocess.check_call
ejecutar comando con argumentos y esperar comando para completar.
shlex.split
divide el cmd de la cadena utilizando una sintaxis similar a la del shell
RESTO DE LOS CASOS:
Si esto no funciona para algún comando específico, lo más probable es que tenga un problema con intérpretes de línea de comandos . El sistema operativo eligió el predeterminado que no es adecuado para su tipo de programa o no pudo encontrar uno adecuado en la ruta ejecutable del sistema.
Ejemplo :
Uso del operador de redirección en un sistema Unix
input_1 = "input_1.txt"
input_2 = "input_2.txt"
output = "merged.txt"
base_command = "/bin/bash -c 'cat {input} > {output}'"
base_command.format(input_1, output=output_1)
subprocess.check_call(shlex.split(base_command))
base_command.format(input_1, output=output_2)
subprocess.check_call(shlex.split(base_command))
Como se indica en El zen de Python :
Explícito es mejor que implícito
Entonces, si usa una función Python> = 3.6, se vería así:
import subprocess
import shlex
def run_command(cmd_interpreter: str, command: str) -> None:
base_command = f"{cmd_interpreter} -c '{command}'"
subprocess.check_call(shlex.split(base_command)
import os
os.system('testing')
os es una biblioteca de python, por lo que es muy importante importar al hacer cualquier prueba.