una Llamando un comando externo en Python




os.system python ejemplos (24)

¿Cómo puedo llamar a un comando externo (como si lo hubiera escrito en el shell de Unix o el símbolo del sistema de Windows) desde un script de Python?


Con la biblioteca estándar

El módulo de subproceso de uso (Python 3):

import subprocess
subprocess.run(['ls', '-l'])

Es la forma estándar recomendada. Sin embargo, las tareas más complicadas (tuberías, salida, entrada, etc.) pueden ser tediosas para construir y escribir.

Nota sobre la versión de Python: si aún está utilizando Python 2, subprocess.call funciona de una manera similar.

ProTip: docs.python.org/2/library/shlex.html#shlex.split puede ayudarlo a analizar el comando para run , call y otras funciones de subprocess en caso de que no quiera (o no pueda) proporcionarlas en forma de listas:

import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))

Con dependencias externas

Si no le importan las dependencias externas, use plumbum :

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

Es la mejor envoltura de subprocess . Es multiplataforma, es decir, funciona tanto en sistemas Windows como Unix. Instalar por pip install plumbum .

Otra biblioteca popular es sh :

from sh import ifconfig
print(ifconfig('wlan0'))

Sin embargo, dejó caer el soporte de Windows, por lo que no es tan impresionante como solía ser. Instalar por pip install sh .


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

Aquí hay un resumen de las formas de llamar a programas externos y las ventajas y desventajas de cada uno:

  1. os.system("some_command with args") pasa el comando y los argumentos al shell de su sistema. Esto es bueno porque realmente puede ejecutar varios 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, aunque esto es conveniente, debe controlar 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 en realidad externos. Consulte la documentación .

  2. 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 el i / o de forma ligeramente diferente. Si pasa todo como una cadena, entonces su comando pasa al shell; Si los pasa como una lista, entonces no necesita preocuparse por escapar de nada. Consulte la documentación .

  3. La clase de Popen del módulo de subprocess . Esto pretende ser un reemplazo para os.popen pero tiene el inconveniente de ser un poco más complicado por el hecho de 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 popen diferentes. Consulte la documentación .

  4. La función de call desde el módulo de subprocess . Básicamente, es igual que la clase Popen y toma todos los mismos argumentos, pero simplemente espera hasta que el comando se completa y te da el código de retorno. Por ejemplo:

    return_code = subprocess.call("echo Hello World", shell=True)  
    

    Consulte la documentación .

  5. Si está en Python 3.5 o posterior, puede usar la nueva función subprocess.run , que es muy parecida a la anterior pero aún más flexible y devuelve un objeto CompletedProcess cuando el comando termina de ejecutarse.

  6. 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 de subprocess probablemente debería ser lo que usas.

Finalmente, tenga en cuenta que para todos los métodos en los que pase el comando final para que sea ejecutado por el shell como una cadena y usted es responsable de evitarlo. Hay serias implicaciones de seguridad si alguna parte de la cadena que pasa no puede ser completamente confiable. Por ejemplo, si un usuario ingresa alguna parte de la cadena. Si no está seguro, solo use estos métodos con constantes. Para darte una pista de las implicaciones, considera este código:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

e imagina que el usuario ingresa "mi mamá no me ama && rm -rf /".


Hay otra diferencia aquí que no se menciona anteriormente.

subprocess.Popen ejecuta el <comando> como un subproceso. En mi caso, necesito ejecutar el archivo <a> que necesita comunicarse con otro programa, <b>.

Intenté subproceso, y la ejecución fue exitosa. Sin embargo, <b> no se pudo comunicar con <a>. Todo es normal cuando ejecuto ambos desde la terminal.

Una más: (NOTA: kwrite se comporta de forma diferente a otras aplicaciones. Si prueba a continuación con Firefox, los resultados no serán los mismos).

Si prueba os.system("kwrite") , el flujo del programa se congela hasta que el usuario cierre kwrite. Para superar eso intenté en os.system(konsole -e kwrite) lugar os.system(konsole -e kwrite) . Esta vez el programa continuó fluyendo, pero kwrite se convirtió en el subproceso de la consola.

Cualquiera que ejecute el archivo kwrite no es un subproceso (es decir, en el monitor del sistema debe aparecer en el extremo izquierdo del árbol).


Siempre uso fabric para 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 Python) .

Mira un ejemplo:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

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

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 de 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 en la mayoría de los shells del sistema y aún desea un acceso conveniente a otras funciones del shell, como shells, comodines de nombres de archivos, expansión de variables de entorno y expansión de ~ a la casa del usuario. directorio. Sin embargo, tenga en cuenta que Python ofrece implementaciones de muchas características similares a shell (en particular, glob , fnmatch , os.walk() , os.path.expandvars() , os.path.expanduser() y shutil ).


Bajo 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 comando at

Un ejemplo con cola de tareas:

import os
os.system('ts <your-command>')

Notas sobre la tarea spooler ( ts):

  1. Puede establecer el número de procesos concurrentes que se ejecutarán ("ranuras") con:

    ts -S <number-of-slots>

  2. La instalación tsno requiere privilegios de administrador. Puedes descargarlo y compilarlo desde la fuente con un simple make, agregarlo a tu ruta y listo.


shlex a usar el subproceso junto con shlex (para manejar el escape de cadenas citadas):

>>> 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)

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 en favor del módulo de subproceso , que otras respuestas han cubierto bien.


subprocess.check_call es conveniente si no desea probar valores de retorno. Se produce una excepción en cualquier error.



Algunos consejos para separar el proceso hijo del que llama (iniciar el proceso hijo en segundo plano).

Supongamos 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 los documentos 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 (win xp), el proceso padre no terminará hasta que longtask.py haya terminado su trabajo. No es lo que quieres en CGI-script. El problema no es específico de Python, en la comunidad PHP los problemas son los mismos.

La solución es pasar el indicador de creación de proceso DETACHED_PROCESS a la función CreateProcess subyacente en la API win. 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 debajo observa que la marca semánticamente correcta es CREATE_NEW_CONSOLE (0x00000010) * /

En freebsd tenemos otro problema: cuando finaliza el proceso principal, también finaliza los procesos secundarios. Y eso tampoco es lo que quieres en el 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 comprobado el código en otras plataformas y no conozco los motivos del comportamiento en freebsd. Si alguien sabe, por favor comparte tus ideas. Buscar en Google al iniciar procesos en segundo plano en Python no arroja ninguna luz todavía.


Hay muchas bibliotecas diferentes que te permiten llamar comandos externos con Python. Para cada biblioteca, proporcioné una descripción y mostré un ejemplo de cómo llamar a un comando externo. El comando que usé como ejemplo es ls -l (listar todos los archivos). Si desea obtener más información sobre cualquiera de las bibliotecas que he enumerado y vinculado la documentación de cada una de ellas.

Fuentes:

Estas son todas las bibliotecas:

Esperemos que esto te ayude a tomar una decisión sobre qué biblioteca usar :)

subproceso

Subproceso le permite llamar a comandos externos y conectarlos a sus tuberías de entrada / salida / error (stdin, stdout y stderr). El subproceso es la opción predeterminada para ejecutar comandos, pero a veces otros módulos son mejores.

subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command

os

os se utiliza para "funcionalidad dependiente del sistema operativo". También se puede usar para llamar a comandos externos con os.system y os.popen (Nota: También hay un subprocess.popen). os siempre ejecutará el shell y es una alternativa simple para las personas que no necesitan o no saben cómo usar subprocess.run .

os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output

sh

sh es una interfaz de subproceso que le permite llamar a programas como si fueran funciones. Esto es útil si desea ejecutar un comando varias veces.

sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function

plomo

Plumbum es una biblioteca para programas Python "tipo script". Puede llamar a programas como funciones como en sh . Plumbum es útil si desea ejecutar una canalización sin el shell.

ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command

espere

pexpect le permite generar aplicaciones secundarias, controlarlas y encontrar patrones en su salida. Esta es una mejor alternativa al subproceso para comandos que esperan un tty en Unix.

pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo [email protected]:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

tela

fabric es una biblioteca Python 2.5 y 2.7. Te permite ejecutar comandos de shell locales y remotos. Fabric es una alternativa simple para ejecutar comandos en un shell seguro (SSH)

fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output

enviado

Enviado es conocido como "subproceso para los humanos". Se utiliza como un envoltorio de conveniencia alrededor del módulo de subprocess .

r = envoy.run("ls -l") # Run command
r.std_out # get output

comandos

commands contienen funciones de envoltorio para os.popen , pero se han eliminado de Python 3 ya que el subprocess es una mejor alternativa.

La edición se basó en el comentario de JF Sebastian.


Mire el módulo de subproceso en la biblioteca estándar:

from subprocess import call
call(["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 módulo de subproceso sobre el os.system () alternativo:

El módulo de subproceso proporciona instalaciones más potentes para generar nuevos procesos y recuperar sus resultados; usar ese módulo es preferible a usar esta función [ os.system() ].

La sección " Reemplazar funciones antiguas con el módulo de subproceso " en la documentación del subproceso puede tener algunas recetas útiles.

Documentación oficial del módulo de subproceso :


os.system no le permite almacenar resultados, por lo tanto, si desea almacenar los resultados en alguna lista o en algo, subprocess.call funciona.


Actualizar:

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 la de Envoy. (Sin embargo, la canalización no es tan sencilla. Vea esta pregunta para saber cómo ).

Aquí hay algunos ejemplos de la documentación .

Ejecutar un proceso:

>>> subprocess.run(["ls", "-l"])  # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

Levantar 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')

Respuesta original:

Recomiendo probar https://github.com/kennethreitz/envoy . Es una envoltura para el subproceso, que a su vez apunta a reemplazar los módulos y funciones más antiguos. Enviado es subproceso para los humanos.

Ejemplo de uso del archivo 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
''

Pipe cosas alrededor también:

>>> r = envoy.run('uptime | pbcopy')

>>> r.command
'pbcopy'
>>> r.status_code
0

>>> r.history
[<Response 'uptime'>]

Enchufe desvergonzado, escribí una biblioteca para esto: P https://github.com/houqp/shell.py

Es básicamente una envoltura para popen y shlex por ahora. También admite comandos de canalización para que pueda encadenar comandos más fácilmente en Python. Así que puedes hacer cosas como:

ex('echo hello shell.py') | "awk '{print $2}'"

Llamando un comando externo en Python

Simple, use subprocess.run , que devuelve un objeto CompletedProcess :

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

¿Por qué?

A partir de Python 3.5, la documentación recomienda subprocess.run :

El enfoque recomendado para invocar subprocesos es usar la función run () para todos los casos de uso que pueda manejar. Para casos de uso más avanzados, la interfaz Popen subyacente se puede utilizar directamente.

Este es un ejemplo del uso más simple posible, y hace exactamente lo que se le pide:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

run espera a que el comando finalice correctamente, luego devuelve un objeto CompletedProcess . En su lugar, puede aumentar TimeoutExpired (si le TimeoutExpired un timeout= argumento) o CalledProcessError (si falla y pasa check=True ).

Como puede inferir del ejemplo anterior, stdout y stderr se canalizan a su propia stdout y stderr de forma predeterminada.

Podemos inspeccionar el objeto devuelto y ver el comando que se dio y el código de retorno:

>>> completed_process.args
'python --version'
>>> completed_process.returncode
0

Captura de salida

Si desea capturar la salida, puede pasar subprocess.PIPE al stderr o stdout apropiado:

>>> cp = subprocess.run('python --version', 
                        stderr=subprocess.PIPE, 
                        stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''

(Me parece interesante y un poco contradictorio que la información de la versión se ponga en stderr en lugar de en stdout).

Pasar una lista de comandos

Uno podría pasar fácilmente de proporcionar manualmente una cadena de comandos (como sugiere la pregunta) a proporcionar una cadena creada mediante programación. No construir cadenas programáticamente. Este es un problema de seguridad potencial. Es mejor asumir que no confías en la entrada.

>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n  This is indented.\r\n'

Tenga en cuenta que solo los args deben pasarse de forma posicional.

Firma completa

Aquí está la firma real en la fuente y como se muestra en la help(run) :

def run(*popenargs, input=None, timeout=None, check=False, **kwargs):

Los popenargs y los kwargs son dados al constructor de Popen . input puede ser una cadena de bytes (o unicode, si se especifica la codificación o universal_newlines=True ) que se canalizará a la entrada estándar del subproceso.

La documentación describe timeout= y check=True mejor de lo que podría:

El argumento de tiempo de espera se pasa a Popen.communicate (). Si el tiempo de espera expira, el proceso hijo se eliminará y se esperará. La excepción TimeoutExpired se volverá a elevar después de que el proceso secundario haya finalizado.

Si la verificación es verdadera y el proceso sale con un código de salida distinto de cero, se generará una excepción CalledProcessError. Los atributos de esa excepción contienen los argumentos, el código de salida y stdout y stderr si fueron capturados.

y este ejemplo para check=True es mejor que uno que podría encontrar:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Firma Expandida

Aquí hay una firma expandida, como se indica en la documentación:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, 
shell=False, cwd=None, timeout=None, check=False, encoding=None, 
errors=None)

Tenga en cuenta que esto indica que solo la lista de argumentos debe pasarse de manera posicional. Así que pase los argumentos restantes como argumentos de palabras clave.

Popen

Cuando se usa Popen en Popen lugar? Me costaría encontrar un caso de uso basado solo en los argumentos. Sin embargo, el uso directo de Popen le daría acceso a sus métodos, incluidos poll , 'send_signal', 'terminate' y 'wait'.

Aquí está la firma de Popen como se indica en la fuente . Creo que esta es la encapsulación más precisa de la información (a diferencia de la help(Popen) ):

def __init__(self, args, bufsize=-1, executable=None,
             stdin=None, stdout=None, stderr=None,
             preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
             shell=False, cwd=None, env=None, universal_newlines=False,
             startupinfo=None, creationflags=0,
             restore_signals=True, start_new_session=False,
             pass_fds=(), *, encoding=None, errors=None):

Pero más informativa es la documentación de Popen :

subprocess.Popen(args, bufsize=-1, executable=None, stdin=None,
                 stdout=None, stderr=None, preexec_fn=None, close_fds=True,
                 shell=False, cwd=None, env=None, universal_newlines=False,
                 startupinfo=None, creationflags=0, restore_signals=True,
                 start_new_session=False, pass_fds=(), *, encoding=None, errors=None)

Ejecutar un programa hijo en un nuevo proceso. En POSIX, la clase usa el comportamiento similar a os.execvp () para ejecutar el programa hijo. En Windows, la clase usa la función CreateProcess () de Windows. Los argumentos a Popen son los siguientes.

La comprensión de la documentación restante en Popen se dejará como un ejercicio para el lector.


Verifique también la biblioteca de 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')

Así es como ejecuto mis comandos. Este código tiene todo lo que necesitas bastante

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()

os.system está bien, pero un poco anticuado. Tampoco es muy seguro. En su lugar, intente subprocess . subprocess no llama directamente a sh y, por lo tanto, es más seguro que os.system .

Obtenga más información here .


Utilizar:

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 , here está la documentación.


Puede ser así de simple:

import os
cmd = "your command"
os.system(cmd)




external