Comment appeler une commande externe (comme si je l'avais tapée au shell Unix ou à l'invite de commande Windows) à partir d'un script Python?

4739
freshWoWer 18 sept. 2008 à 05:35

27 réponses

Meilleure réponse

Regardez le module sous-processus dans la bibliothèque standard:

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

L'avantage de subprocess par rapport à system est qu'il est plus flexible (vous pouvez obtenir le stdout, stderr, le "vrai" code d'état, une meilleure gestion des erreurs, etc. ...).

La documentation officielle recommande le module subprocess par rapport à l'alternative { {X1}}:

Le module subprocess fournit des fonctionnalités plus puissantes pour générer de nouveaux processus et récupérer leurs résultats; l'utilisation de ce module est préférable à l'utilisation de cette fonction [os.system() ].

Remplacement de fonctions plus anciennes par le module de sous-processus dans la documentation subprocess peut contenir des recettes utiles.

Pour les versions de Python antérieures à 3.5, utilisez call:

import subprocess
subprocess.call(["ls", "-l"])
4552
Corey Goldberg 31 août 2019 à 03:17

J'ai tendance à utiliser le sous-processus avec shlex (pour gérer l'échappement des chaînes entre guillemets):

>>> 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 avril 2014 à 14:37

subprocess.check_call est pratique si vous ne souhaitez pas tester les valeurs de retour. Il lève une exception sur toute erreur.

21
cdunn2001 18 janv. 2011 à 19:21

Quelques conseils pour détacher le processus enfant de l'appelant (démarrage du processus enfant en arrière-plan).

Supposons que vous souhaitiez démarrer une longue tâche à partir d'un script CGI. Autrement dit, le processus enfant doit vivre plus longtemps que le processus d'exécution de script CGI.

L'exemple classique de la documentation du module de sous-processus est:

import subprocess
import sys

# Some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess

# Some more code here

L'idée ici est que vous ne voulez pas attendre dans la ligne 'call subprocess' jusqu'à ce que le longtask.py soit terminé. Mais on ne sait pas ce qui se passe après la ligne «un peu plus de code ici» de l'exemple.

Ma plate-forme cible était FreeBSD, mais le développement était sur Windows, donc j'ai d'abord rencontré le problème sur Windows.

Sous Windows (Windows XP), le processus parent ne se terminera pas tant que le fichier longtask.py n'aura pas terminé son travail. Ce n'est pas ce que vous voulez dans un script CGI. Le problème n'est pas spécifique à Python; dans la communauté PHP, les problèmes sont les mêmes.

La solution consiste à passer DETACHED_PROCESS Création de processus Marquer à la fonction CreateProcess sous-jacente dans l'API Windows. Si vous avez installé pywin32, vous pouvez importer l'indicateur du module win32process, sinon vous devez le définir vous-même:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksun dans un commentaire ci-dessous note que l'indicateur sémantiquement correct est CREATE_NEW_CONSOLE (0x00000010) * /

Sur FreeBSD, nous avons un autre problème: lorsque le processus parent est terminé, il termine également les processus enfants. Et ce n'est pas non plus ce que vous voulez dans un script CGI. Certaines expériences ont montré que le problème semblait être lié au partage de sys.stdout. Et la solution de travail était la suivante:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

Je n'ai pas vérifié le code sur d'autres plateformes et je ne connais pas les raisons du comportement sur FreeBSD. Si quelqu'un le sait, partagez vos idées. Googler sur le démarrage de processus d'arrière-plan en Python ne fait pas encore la lumière.

220
Peter Mortensen 29 nov. 2019 à 21:46

Avec la bibliothèque standard

Utilisez le module de sous-processus (Python 3):

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

C'est la voie standard recommandée. Cependant, des tâches plus compliquées (canaux, sortie, entrée, etc.) peuvent être fastidieuses à construire et à écrire.

Remarque sur la version Python: si vous utilisez toujours Python 2, sous-processus .call fonctionne de la même manière.

ProTip: shlex.split peut vous aider à analyser la commande pour run, call et d'autres fonctions subprocess au cas où vous ne voudriez pas (ou vous ne pouvez pas!) les fournir sous forme de listes:

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

Avec des dépendances externes

Si cela ne vous dérange pas, utilisez les plumbum:

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

C'est le meilleur wrapper subprocess. Il est multiplateforme, c'est-à-dire qu'il fonctionne à la fois sur les systèmes Windows et Unix. Installez d'ici pip install plumbum.

Une autre bibliothèque populaire est sh:

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

Cependant, sh a abandonné la prise en charge de Windows, donc ce n'est plus aussi génial qu'avant. Installez d'ici pip install sh.

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

Si vous avez besoin de la sortie de la commande que vous appelez, vous pouvez ensuite utiliser 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'

Notez également le paramètre shell.

Si le shell est True, la commande spécifiée sera exécutée via le shell. Cela peut être utile si vous utilisez Python principalement pour le flux de contrôle amélioré qu'il offre sur la plupart des shells du système et que vous souhaitez toujours accéder facilement à d'autres fonctionnalités de shell telles que les tuyaux de shell, les caractères génériques de nom de fichier, l'expansion des variables d'environnement et l'extension de ~ au domicile d'un utilisateur annuaire. Cependant, notez que Python lui-même propose des implémentations de nombreuses fonctionnalités de type shell (en particulier, glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser(), os.path.expanduser(), et { {X6}}).

67
Peter Mortensen 3 juin 2018 à 20:18

Cela peut être aussi simple:

import os
cmd = "your command"
os.system(cmd)
27
Samadi Salahedine 8 juin 2018 à 12:06

Appel d'une commande externe en Python

Simple, utilisez subprocess.run, qui retourne un objet 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)

Pourquoi?

Depuis Python 3.5, la documentation recommande subprocess.run:

L'approche recommandée pour invoquer des sous-processus consiste à utiliser la fonction run () pour tous les cas d'utilisation qu'elle peut gérer. Pour des cas d'utilisation plus avancés, l'interface Popen sous-jacente peut être utilisée directement.

Voici un exemple d'utilisation la plus simple possible - et il fait exactement comme demandé:

>>> 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 attend que la commande se termine avec succès, puis renvoie un objet CompletedProcess. Il peut à la place lever TimeoutExpired (si vous lui donnez un argument timeout=) ou CalledProcessError (s'il échoue et que vous passez check=True).

Comme vous pouvez le déduire de l'exemple ci-dessus, stdout et stderr sont tous deux dirigés par défaut vers vos propres stdout et stderr par défaut.

Nous pouvons inspecter l'objet retourné et voir la commande qui a été donnée et le code retour:

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

Capture de sortie

Si vous souhaitez capturer la sortie, vous pouvez passer subprocess.PIPE aux stderr ou stdout appropriés:

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

(Je trouve intéressant et légèrement contre-intuitif que les informations de version soient mises sur stderr au lieu de stdout.)

Passer une liste de commandes

On peut facilement passer de la fourniture manuelle d'une chaîne de commande (comme le suggère la question) à la fourniture d'une chaîne construite par programme. Ne créez pas de chaînes par programmation. Il s'agit d'un problème de sécurité potentiel. Il vaut mieux supposer que vous ne faites pas confiance à l'entrée.

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

Remarque, seul args doit être transmis en position.

Signature complète

Voici la signature réelle dans la source et comme indiqué par help(run):

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

Les popenargs et kwargs sont donnés au constructeur Popen. input peut être une chaîne d'octets (ou unicode, si spécifiez le codage ou universal_newlines=True) qui sera redirigée vers le stdin du sous-processus.

La documentation décrit timeout= et check=True mieux que moi:

L'argument timeout est passé à Popen.communicate (). Si le délai expire, le processus enfant sera tué et attendu. L'exception TimeoutExpired sera relancée une fois le processus enfant terminé.

Si la vérification est vraie et que le processus se termine avec un code de sortie différent de zéro, une exception CalledProcessError sera déclenchée. Les attributs de cette exception contiennent les arguments, le code de sortie et stdout et stderr s'ils ont été capturés.

Et cet exemple pour check=True est meilleur que celui que j'ai pu trouver:

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

Signature étendue

Voici une signature étendue, comme indiqué dans la documentation:

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

Notez que cela indique que seule la liste d'arguments doit être passée positionnellement. Passez donc les arguments restants comme arguments de mots clés.

Popen

Quand utiliser Popen à la place? J'aurais du mal à trouver un cas d'utilisation basé uniquement sur les arguments. L'utilisation directe de Popen vous donnerait cependant accès à ses méthodes, notamment poll, 'send_signal', 'terminate' et 'wait'.

Voici la signature Popen donnée dans la source. Je pense que c'est l'encapsulation la plus précise de l'information (par opposition à 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):

Mais plus informative est la documentation 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)

Exécutez un programme enfant dans un nouveau processus. Sur POSIX, la classe utilise un comportement semblable à os.execvp () pour exécuter le programme enfant. Sous Windows, la classe utilise la fonction Windows CreateProcess (). Les arguments de Popen sont les suivants.

Comprendre la documentation restante sur Popen restera un exercice pour le lecteur.

34
Aaron Hall 18 oct. 2017 à 16:37

Je recommande d'utiliser le module sous-processus au lieu de os.system car il échappe le shell pour vous et est donc beaucoup plus sûr.

subprocess.call(['ping', 'localhost'])
139
Nicolas Gervais 24 févr. 2020 à 01:01

Sous Windows, vous pouvez simplement importer le module subprocess et exécuter des commandes externes en appelant subprocess.Popen(), subprocess.Popen().communicate() et subprocess.Popen().wait() comme ci-dessous:

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

Production:

This process detail:
python.exe                     604 RDP-Tcp#0                  4      5,660 K
16
Peter Mortensen 28 mai 2017 à 23:08

Utilisation:

import os

cmd = 'ls -al'

os.system(cmd)

os - Ce module fournit une manière portable d'utiliser les fonctionnalités dépendantes du système d'exploitation.

Pour les autres os fonctions, ici est la documentation.

27
Peter Mortensen 28 mai 2017 à 23:05

Voici comment j'exécute mes commandes. Ce code a à peu près tout ce dont vous avez besoin

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 à 05:44

Il y a aussi 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 à 17:41

Vérifiez également la bibliothèque Python "pexpect".

Il permet un contrôle interactif des programmes / commandes externes, même ssh, ftp, telnet, etc. Vous pouvez simplement taper quelque chose comme:

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')
71
Peter Mortensen 28 mai 2017 à 23:02

J'utilise toujours fabric pour ces choses comme:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

Mais cela semble être un bon outil: sh (interface de sous-processus Python).

Regardez un exemple:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)
71
Peter Mortensen 29 nov. 2019 à 21:47
import os
cmd = 'ls -al'
os.system(cmd)

Si vous souhaitez renvoyer les résultats de la commande, vous pouvez utiliser {{X0 }}. Cependant, cela est obsolète depuis la version 2.6 en faveur du module de sous-processus , que d'autres réponses ont bien couvert.

136
Patrick M 26 janv. 2016 à 16:53

Mise à jour:

subprocess.run est l'approche recommandée à partir de Python 3.5 si votre code n'a pas besoin de maintenir la compatibilité avec les versions antérieures de Python. Il est plus cohérent et offre une facilité d'utilisation similaire à celle d'Envoy. (La tuyauterie n'est cependant pas aussi simple. Voir cette question pour savoir comment.)

Voici quelques exemples de la documentation.

Exécutez un processus:

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

Augmenter en cas d'échec:

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

Sortie de capture:

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

Réponse originale:

Je recommande d'essayer Envoy. C'est un wrapper pour le sous-processus, qui à son tour vise à remplacer les anciens modules et les fonctions. Envoy est un sous-processus pour les humains.

Exemple d'utilisation de le 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
''

Tuyau aussi:

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

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

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

J'aime bien shell_command pour sa simplicité. Il est construit au-dessus du module de sous-processus.

Voici un exemple tiré de la documentation:

>>> 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 à 21:49

Plug sans vergogne, j'ai écrit une bibliothèque pour ça: P https://github.com/houqp/shell.py

C'est essentiellement un emballage pour popen et shlex pour l'instant. Il prend également en charge les commandes de canalisation afin que vous puissiez enchaîner les commandes plus facilement en Python. Vous pouvez donc faire des choses comme:

ex('echo hello shell.py') | "awk '{print $2}'"
16
houqp 1 mai 2014 à 20:49
import os
os.system("your command")

Notez que cela est dangereux, car la commande n'est pas nettoyée. Je vous laisse le soin de google pour la documentation pertinente sur les modules 'os' et 'sys'. Il existe un tas de fonctions (exec * et spawn *) qui feront des choses similaires.

139
Peter Mortensen 3 juin 2018 à 17:10

Utilisez le module os:

import os
os.system("your command")

Par exemple,

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

Vous pouvez utiliser Popen, puis vous pouvez vérifier l'état de la procédure:

from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()

Consultez subprocess.Popen.

15
Peter Mortensen 28 mai 2017 à 23:01

os.system est OK, mais un peu daté. Ce n'est pas non plus très sûr. Essayez plutôt subprocess. subprocess n'appelle pas sh directement et est donc plus sécurisé que os.system.

Obtenez plus d'informations ici.

34
Dimitris Fasarakis Hilliard 10 déc. 2016 à 13:25

Implémentation typique:

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

Vous êtes libre de faire ce que vous voulez avec les données stdout dans le canal. En fait, vous pouvez simplement omettre ces paramètres (stdout= et stderr=) et cela se comportera comme os.system().

344
Trenton McKinney 4 oct. 2019 à 02:33

Utilisez le sous-processus .

... ou pour une commande très simple:

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

os.system ne vous permet pas de stocker les résultats, donc si vous souhaitez stocker les résultats dans une liste ou quelque chose, un subprocess.call fonctionne.

21
Peter Mortensen 29 nov. 2019 à 21:48

Sans la sortie du résultat:

import os
os.system("your command here")

Avec sortie du résultat:

import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")
42
Peter Mortensen 28 mai 2017 à 22:59
89228