¿Cómo se llama 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?

5151
freshWoWer 18 sep. 2008 a las 05:35

27 respuestas

La mejor respuesta

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", un mejor manejo de errores, etc. ...)

La documentación oficial recomienda el módulo subprocess sobre la alternativa { {X1}}:

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 Reemplazo de funciones anteriores con el módulo de subproceso en la documentación de subprocess puede tener algunas recetas útiles.

Para las versiones de Python anteriores a 3.5, use call:

import subprocess
subprocess.call(["ls", "-l"])
4552
Corey Goldberg 31 ago. 2019 a las 03:17

Tiendo a usar 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)
21
Emil Stenström 30 abr. 2014 a las 14:37

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

21
cdunn2001 18 ene. 2011 a las 19:21

Algunas sugerencias para separar el proceso hijo del que llama (iniciando 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 primero enfrenté el problema en Windows.

En Windows (Windows XP), el proceso principal 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 Creación de procesos Marque 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 los procesos hijos. 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 comprobado 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 luz.

220
Peter Mortensen 29 nov. 2019 a las 21:46

Con la biblioteca estándar

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 está utilizando Python 2, subproceso .call funciona de manera similar.

Sugerencia: shlex.split puede ayudarlo a analizar el comando para run, call y otras subprocess funciones en caso de que no desee (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 el mejor contenedor subprocess. Es multiplataforma, es decir, funciona en sistemas similares a 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.

67
6 revs, 2 users 79% 29 nov. 2019 a las 21:54

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 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 casa de un usuario directorio. Sin embargo, tenga en cuenta que Python en sí ofrece implementaciones de muchas características similares a las de shell (en particular, glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser() y { {X6}}).

67
Peter Mortensen 3 jun. 2018 a las 20:18

Puede ser así de simple:

import os
cmd = "your command"
os.system(cmd)
27
Samadi Salahedine 8 jun. 2018 a las 12:06

Llamar a 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 utilizar la función run () para todos los casos de uso que puede manejar. Para casos de uso más avanzados, la interfaz de Popen subyacente se puede usar directamente.

Aquí hay 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 con éxito, luego devuelve un objeto CompletedProcess. En su lugar, puede aumentar TimeoutExpired (si le da un argumento timeout=) o CalledProcessError (si falla y pasa check=True).

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

Podemos inspeccionar el objeto devuelto y ver el comando que se le 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 a la stderr o stdout apropiada:

>>> 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 ligeramente contradictorio que la información de la versión se ponga en stderr en lugar de stdout).

Pase una lista de comandos

Uno podría pasar fácilmente de proporcionar manualmente una cadena de comando (como sugiere la pregunta) a proporcionar una cadena construida programáticamente. No cree cadenas mediante programación. Este es un posible problema de seguridad. Es mejor asumir que no confía 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 args debe pasarse posicionalmente.

Firma completa

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

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

Los popenargs y kwargs se dan al constructor Popen. input puede ser una cadena de bytes (o unicode, si se especifica la codificación o universal_newlines=True) que se canalizarán al stdin 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 secundario se cancelará y se esperará. La excepción TimeoutExpired se volverá a generar después de que el proceso secundario haya finalizado.

Si la comprobació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 se me ocurra:

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

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 posicionalmente. Entonces pase los argumentos restantes como argumentos de palabras clave.

Popen

¿Cuándo usar Popen en su lugar? Me gustarí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', 'terminar' y 'esperar'.

Aquí está la firma Popen tal como se proporciona en la fuente. Creo que esta es la encapsulación más precisa de la información (a diferencia de 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 informativo es la documentación 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)

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

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

34
Aaron Hall 18 oct. 2017 a las 16:37

Recomiendo usar el módulo de subproceso en lugar de os.system porque escapa el shell para ti y por lo tanto es mucho más seguro.

subprocess.call(['ping', 'localhost'])
139
Nicolas Gervais 24 feb. 2020 a las 01:01

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
16
Peter Mortensen 28 may. 2017 a las 23:08

Uso:

import os

cmd = 'ls -al'

os.system(cmd)

os: este módulo proporciona una forma portátil de utilizar la funcionalidad que depende del sistema operativo.

Para las funciones más os, aquí es la documentación.

27
Peter Mortensen 28 may. 2017 a las 23:05

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()
54
Usman Khan 28 oct. 2012 a las 05:44

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
32
stuckintheshuck 10 oct. 2014 a las 17:41

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')
71
Peter Mortensen 28 may. 2017 a las 23:02

Siempre uso fabric para esto 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)
71
Peter Mortensen 29 nov. 2019 a las 21:47
import os
cmd = 'ls -al'
os.system(cmd)

Si desea devolver los resultados del comando, puede usar {{X0 }}. 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.

136
Patrick M 26 ene. 2016 a las 16:53

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

Elevar 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 Envoy. Es un contenedor para subprocesos, que a su vez tiene como objetivo reemplazar los módulos más antiguos y funciones El 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 cosas alrededor también:

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

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

>>> r.history
[<Response 'uptime'>]
52
Peter Mortensen 29 nov. 2019 a las 21:52

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
23
Peter Mortensen 29 nov. 2019 a las 21:49

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

Básicamente es 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. Entonces puedes hacer cosas como:

ex('echo hello shell.py') | "awk '{print $2}'"
16
houqp 1 may. 2014 a las 20:49
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.

139
Peter Mortensen 3 jun. 2018 a las 17:10

Utilice el os module:

import os
os.system("your command")

Por ejemplo,

import os
os.system("ifconfig")
25
Peter Mortensen 29 nov. 2019 a las 22:09

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.

15
Peter Mortensen 28 may. 2017 a las 23:01

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í.

34
Dimitris Fasarakis Hilliard 10 dic. 2016 a las 13:25

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

344
Trenton McKinney 4 oct. 2019 a las 02:33

Utilice subproceso .

... o para un comando muy simple:

import os
os.system('cat testfile')
39
Peter Mortensen 29 nov. 2019 a las 21:39

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

21
Peter Mortensen 29 nov. 2019 a las 21:48

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")
42
Peter Mortensen 28 may. 2017 a las 22:59