¿Cómo ejecuto un programa o llamo a un comando del sistema?

12 minutos de lectura

avatar de usuario
frescoWoWer

¿Cómo llamo a un comando externo dentro de Python como si lo hubiera escrito en un shell o símbolo del sistema?

  • no lo entiendo que tiene de malo import os; os.system('pip list | grep anatome')? Al menos eso le permite hacer tuberías como muestra mi ejemplo. No está claro cómo hacer eso con import subprocess; subprocess.run(["ls", "-l"]).

    –Charlie Parker

    12 de noviembre de 2021 a las 16:58

avatar de usuario
eli cortesano

Resumen de formas de llamar a programas externos, incluidas sus ventajas y desventajas:

  1. os.system pasa el comando y los argumentos al shell de su sistema. Esto es bueno porque en realidad puede ejecutar varios comandos a la vez de esta manera y configurar canalizaciones 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 los 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.

  2. os.popen hará lo mismo que os.system excepto que le brinda 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, entonces su comando se pasa al shell; si los pasa como una lista, entonces no necesita preocuparse por escapar de nada. Ejemplo:

    print(os.popen("ls -l").read())
    
  3. subprocess.Popen. Esto está pensado como un reemplazo para os.popen, pero tiene la desventaja de ser un poco más complicado por ser tan completo. Por ejemplo, dirías:

    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 emergentes diferentes. Ver la documentación.

  4. subprocess.call. Esto es básicamente como el Popen class y toma todos los mismos argumentos, pero simplemente espera hasta que el comando se completa y le proporciona el código de retorno. Por ejemplo:

    return_code = subprocess.call("echo Hello World", shell=True)
    
  5. subprocess.run. Solo Python 3.5+. Similar al anterior pero aún más flexible y devuelve un CompletedProcess objeto cuando el comando termine de ejecutarse.

  6. os.fork, os.exec, os.spawn son similares a sus contrapartes del lenguaje C, pero no recomiendo usarlos directamente.

los subprocess El módulo probablemente debería ser lo que usas.

Finalmente, tenga en cuenta que para todos los métodos en los que pasa el comando final, el shell lo ejecutará como una cadena y usted es responsable de escaparlo. Hay serias implicaciones de seguridad. si no se puede confiar plenamente en alguna parte de la cadena que pasa. Por ejemplo, si un usuario ingresa alguna/cualquier parte de la cadena. Si no está seguro, utilice estos métodos solo 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 imagina que el usuario ingresa algo”my mama didnt love me && 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/… “Estilísticamente, Perl y Python tienen filosofías diferentes. El lema más conocido de Perl es “Hay más de una forma de hacerlo”. Python está diseñado para tener una forma obvia de hacerlo” ¡Parece que debería ser al revés! En Perl, solo conozco dos formas de ejecutar un comando: usar una marca de retroceso o open.

    – Jean

    26 mayo 2015 a las 21:16

  • Si usa Python 3.5+, use subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run

    – fénix

    7 oct 2015 a las 16:37

  • Lo que normalmente se necesita saber es qué se hace con STDOUT y STDERR del proceso secundario, porque si se ignoran, en algunas condiciones (bastante comunes), eventualmente el proceso secundario emitirá una llamada al 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 desde ese búfer. Entonces, con las formas recomendadas actualmente, subprocess.run(..)que hace exactamente “Esto no captura stdout o stderr de forma predeterminada”. ¿implicar? Qué pasa subprocess.check_output(..) y STDERR?

    –Evgeni Sergeev

    1 de junio de 2016 a las 10:44

  • ¿Cuál de los comandos que recomendó bloquear mi script? es decir, si quiero ejecutar varios comandos en un for bucle, ¿cómo lo hago sin que bloquee mi script de python? No me importa el resultado del comando, solo quiero ejecutar muchos de ellos.

    –Charlie Parker

    24/10/2017 a las 19:08

  • Podría decirse que esto es al revés. La mayoría de la gente sólo necesita subprocess.run() o sus hermanos mayores subprocess.check_call() et al. Para los casos en que estos no sean suficientes, consulte subprocess.Popen(). os.popen() tal vez no debería mencionarse en absoluto, o aparecer incluso después de “hackear su propio código fork/exec/spawn”.

    – triplete

    3 de diciembre de 2018 a las 6:00


  • .readlines() lee todos líneas a la vez, es decir, se bloquea hasta que el subproceso sale (cierra su extremo de la tubería). Para leer en tiempo real (si no hay problemas de almacenamiento en búfer), podría: for line in iter(p.stdout.readline, ''): print line,

    – jfs

    16 de noviembre de 2012 a las 14:12

  • ¿Podría dar más detalles sobre lo que quiere decir con “si no hay problemas de almacenamiento en búfer”? Si el proceso se bloquea definitivamente, la llamada al 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?

    – EmmEff

    17 de noviembre de 2012 a las 13:25

  • el proceso secundario puede usar el almacenamiento en búfer de bloque en modo no interactivo en lugar del almacenamiento en búfer de línea, por lo que p.stdout.readline() (nota: no s al final) no verá ningún dato hasta que el niño llene 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 simplemente una tubería (popen())?. En esta respuesta se proporcionan algunas soluciones (pexpect, pty, stdbuf)

    – jfs

    17 de noviembre de 2012 a las 13:51

  • el problema del almacenamiento en búfer solo importa si desea una salida en tiempo real y no se aplica a su código que no imprime nada hasta todos se reciben datos

    – jfs

    17 de noviembre de 2012 a las 13:53

  • Esta respuesta estuvo bien para su época, pero ya no deberíamos recomendar Popen para tareas sencillas. Esto también especifica innecesariamente shell=True. Prueba uno de los subprocess.run() respuestas

    – triplete

    3 de diciembre de 2018 a las 5:39

avatar de usuario
nuevo

Algunos consejos sobre cómo separar el proceso secundario del que llama (iniciar el proceso secundario en segundo plano).

Suponga que desea iniciar una tarea larga desde un script CGI. Es decir, el proceso hijo debería durar más 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 ‘subproceso de llamada’ 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 primero enfrenté el problema en Windows.

En Windows (Windows XP), el proceso principal no finalizará hasta que longtask.py haya terminado su trabajo. No es lo que quieres en un guión 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 procesos a la función CreateProcess subyacente en la API de Windows. Si tiene instalado pywin32, puede importar el indicador desde el módulo win32process, 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 a continuación señala que el indicador semánticamente correcto es CREATE_NEW_CONSOLE (0x00000010) */

En FreeBSD tenemos otro problema: cuando el proceso principal finaliza, también finaliza los procesos secundarios. Y eso tampoco es lo que quieres en un guión 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 comprobado el código en otras plataformas y no sé las razones del comportamiento en FreeBSD. Si alguien sabe, por favor comparta sus ideas. Buscar en Google sobre el inicio de procesos en segundo plano en Python aún no arroja ninguna luz.

  • es posible que también necesite el indicador CREATE_NEW_PROCESS_GROUP. Ver Popen esperando el proceso del niño incluso cuando el niño inmediato ha terminado

    – jfs

    16 de noviembre de 2012 a las 14:16

  • estoy viendo import subprocess as sp;sp.Popen('calc') sin esperar a que se complete el subproceso. Parece que las banderas de creación no son necesarias. ¿Qué me estoy perdiendo?

    – ubershmekel

    27/10/2014 a las 21:01


  • @ubershmekel, no estoy seguro de lo que quieres decir y no tengo una instalación de Windows. Si no recuerdo mal, sin las banderas no puedes cerrar el cmd instancia desde la que inició el calc.

    – newtover

    28/10/2014 a las 12:25

  • Lo siguiente es incorrecto: “[o]n windows (win xp), el proceso principal no finalizará hasta que longtask.py haya terminado su trabajo”. El principal se cerrará normalmente, pero la ventana de la consola (instancia de conhost.exe) solo se cerrará cuando finalice el último proceso adjunto, y el niño puede haber heredado la consola de los padres. DETACHED_PROCESS en creationflags evita esto evitando que el hijo herede o creando una consola. Si en cambio quieres una nueva consola, usa CREATE_NEW_CONSOLE (0x00000010).

    –Eryk Sun

    27 de octubre de 2015 a las 0:27

  • No quise decir que ejecutar como un proceso separado es incorrecto. Dicho esto, es posible que deba configurar los identificadores estándar para archivos, tuberías o os.devnull porque algunos programas de consola salen con un error de lo contrario. Cree una nueva consola cuando desee que el proceso secundario interactúe con el usuario al mismo tiempo que el proceso principal. Sería confuso tratar de hacer ambas cosas en una sola ventana.

    –Eryk Sun

    27/10/2015 a las 17:37

avatar de usuario
Pedro Mortensen

import os
os.system("your command")

Tenga en cuenta que esto es peligroso, ya que el comando no se limpia. Le dejo a usted que busque en 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.

  • es posible que también necesite el indicador CREATE_NEW_PROCESS_GROUP. Ver Popen esperando el proceso del niño incluso cuando el niño inmediato ha terminado

    – jfs

    16 de noviembre de 2012 a las 14:16

  • estoy viendo import subprocess as sp;sp.Popen('calc') sin esperar a que se complete el subproceso. Parece que las banderas de creación no son necesarias. ¿Qué me estoy perdiendo?

    – ubershmekel

    27/10/2014 a las 21:01


  • @ubershmekel, no estoy seguro de lo que quieres decir y no tengo una instalación de Windows. Si no recuerdo mal, sin las banderas no puedes cerrar el cmd instancia desde la que inició el calc.

    – newtover

    28/10/2014 a las 12:25

  • Lo siguiente es incorrecto: “[o]n windows (win xp), el proceso principal no finalizará hasta que longtask.py haya terminado su trabajo”. El principal se cerrará normalmente, pero la ventana de la consola (instancia de conhost.exe) solo se cerrará cuando finalice el último proceso adjunto, y el niño puede haber heredado la consola de los padres. DETACHED_PROCESS en creationflags evita esto evitando que el hijo herede o creando una consola. Si en cambio quieres una nueva consola, usa CREATE_NEW_CONSOLE (0x00000010).

    –Eryk Sun

    27 de octubre de 2015 a las 0:27

  • No quise decir que ejecutar como un proceso separado es incorrecto. Dicho esto, es posible que deba configurar los identificadores estándar para archivos, tuberías o os.devnull porque algunos programas de consola salen con un error de lo contrario. Cree una nueva consola cuando desee que el proceso secundario interactúe con el usuario al mismo tiempo que el proceso principal. Sería confuso tratar de hacer ambas cosas en una sola ventana.

    –Eryk Sun

    27/10/2015 a las 17:37

avatar de usuario
Nicolás Gervais

Yo recomendaría usar el subproceso module en lugar de os.system porque escapa por usted y, por lo tanto, es mucho más seguro.

subprocess.call(['ping', 'localhost'])

  • Si quieres crear una lista a partir de un comando con parámetrosuna lista que se puede utilizar con subprocess Cuándo shell=Falseluego usa shlex.split 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)

    – Daniel F.

    20 sep 2018 a las 18:07


  • Esto es incorrecto: “se escapa por ti 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.

    – miente ryan

    4 de diciembre de 2018 a las 8:36


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad