5900 votes

Comment exécuter un programme ou appeler une commande système ?

Comment puis-je appeler une commande externe dans Python comme si je l'avais tapée dans un shell ou une invite de commande?

0 votes

Pour ceux qui recherchent un cours de Python gratuit et interactif, voici : theconstructsim.com/robotigniteacademy_learnros/…

0 votes

Je ne comprends pas, quel est le problème avec import os; os.system('pip list | grep anatome')? Au moins cela vous permet de faire du piping comme le montre mon exemple. Il n'est pas clair comment faire cela avec import subprocess; subprocess.run(["ls", "-l"]).

5605voto

David Cournapeau Points 21956

Utilisez subprocess.run:

import subprocess

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

Une autre méthode commune est os.system mais vous ne devriez pas l'utiliser car elle est dangereuse si certaines parties de la commande proviennent de l'extérieur de votre programme ou peuvent contenir des espaces ou d'autres caractères spéciaux, de plus subprocess.run est généralement plus souple (vous pouvez obtenir le stdout, le stderr, le "vrai" code de statut, une meilleure gestion des erreurs, etc.). Même la documentation pour os.system recommande d'utiliser subprocess à la place.

Sur Python 3.4 et antérieur, utilisez subprocess.call à la place de .run:

subprocess.call(["ls", "-l"])

7 votes

Y a-t-il un moyen d'utiliser une substitution de variable? IE j'ai essayé de faire echo $PATH en utilisant call(["echo", "$PATH"]), mais cela a simplement affiché la chaîne littérale $PATH au lieu d'effectuer une substitution. Je sais que je pourrais obtenir la variable d'environnement PATH, mais je me demande s'il existe un moyen facile d'avoir la commande se comporter exactement comme si je l'avais exécutée dans bash.

0 votes

@KevinWheeler Vous devrez utiliser shell=True pour que cela fonctionne.

69 votes

@KevinWheeler Vous ne devriez PAS utiliser shell=True, à cette fin Python dispose de os.path.expandvars. Dans votre cas, vous pouvez écrire : os.path.expandvars("$PATH"). @SethMMorton veuillez reconsidérer votre commentaire -> Pourquoi ne pas utiliser shell=True

3429voto

Eli Courtwright Points 53071

Voici un résumé des façons d'appeler des programmes externes, y compris leurs avantages et leurs inconvénients :

  1. os.system passe la commande et les arguments à l'interpréteur de commandes de votre système. C'est agréable car vous pouvez réellement exécuter plusieurs commandes à la fois de cette manière et configurer des tubes et des redirections d'entrée/sortie. Par exemple :

    os.system("some_command < input_file | another_command > output_file")  

    Cependant, bien que ce soit pratique, vous devez gérer manuellement l'échappement des caractères du shell tels que les espaces, etc. D'autre part, cela vous permet également d'exécuter des commandes qui sont simplement des commandes shell et non réellement des programmes externes.

  2. os.popen fera la même chose que os.system sauf qu'il vous donne un objet ressemblant à un fichier que vous pouvez utiliser pour accéder à l'entrée/sortie standard de ce processus. Il existe 3 autres variantes de popen qui gèrent tous légèrement différemment l'entrée/sortie. Si vous passez tout en tant que chaîne, alors votre commande est passée au shell ; si vous les passez en tant que liste alors vous n'avez pas à vous soucier de l'échappement de quoi que ce soit. Exemple :

    print(os.popen("ls -l").read())
  3. subprocess.Popen. C'est censé être un remplacement pour os.popen, mais a l'inconvénient d'être légèrement plus compliqué en raison de sa complétude. Par exemple, vous diriez :

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()

    au lieu de

    print os.popen("echo Hello World").read()

    mais il est agréable d'avoir toutes les options là dans une seule classe unifiée au lieu de 4 fonctions popen différentes. Voir la documentation.

  4. subprocess.call. C'est essentiellement comme la classe Popen et prend tous les mêmes arguments, mais il attend simplement que la commande se termine et vous donne le code de retour. Par exemple :

    return_code = subprocess.call("echo Hello World", shell=True)
  5. subprocess.run. Uniquement pour Python 3.5+. Similaire aux exemples ci-dessus mais encore plus flexible et renvoie un objet CompletedProcess lorsque la commande a fini de s'exécuter.

  6. os.fork, os.exec, os.spawn sont similaires à leurs homologues du langage C, mais je ne les recommande pas en les utilisant directement.

Le module subprocess est probablement ce que vous devriez utiliser.

Enfin, veuillez noter que pour toutes les méthodes où vous passez la commande finale à exécuter par le shell en tant que chaîne et vous êtes responsable de l'échappement. Il y a de sérieuses implications en termes de sécurité si une partie de la chaîne que vous passez n'est pas entièrement fiable. Par exemple, si un utilisateur entre une partie de la chaîne. Si vous n'êtes pas sûr, utilisez uniquement ces méthodes avec des constantes. Pour vous donner une idée des implications, considérez ce code :

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

et imaginez que l'utilisateur entre quelque chose "ma maman ne m'a pas aimé && rm -rf /" qui pourrait effacer tout le système de fichiers.

50 votes

Belle réponse/explication. Comment cette réponse justifie-t-elle la devise de Python telle que décrite dans cet article ? fastcompany.com/3026446/… "Stylistiquement, Perl et Python ont des philosophies différentes. Le slogan le plus connu de Perl est " Il y a plus d'une façon de le faire". Python est conçu pour avoir une seule façon évidente de le faire" Il semble que cela devrait être le contraire ! En Perl, je ne connais que deux façons d'exécuter une commande - en utilisant des back-ticks ou open.

28 votes

Si vous utilisez Python 3.5+, utilisez subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run

10 votes

Ce dont on a généralement besoin de savoir, c'est ce qui est fait avec la STDOUT et STDERR du processus enfant, car si elles sont ignorées, dans certaines conditions (assez courantes), éventuellement le processus enfant émettra un appel système pour écrire sur la STDOUT (STDERR aussi ?) qui dépasserait le tampon de sortie fourni pour le processus par le système d'exploitation, et le système d'exploitation le bloquera jusqu'à ce qu'un processus lise à partir de ce tampon. Donc, avec les façons actuellement recommandées, subprocess.run(..), que signifie exactement "Cela ne capture pas stdout ou stderr par défaut." ? Et concernant subprocess.check_output(..) et STDERR ?

427voto

EmmEff Points 1789

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 de stdout dans le tuyau. En fait, vous pouvez simplement omettre ces paramètres (stdout= et stderr=) et cela se comportera comme os.system().

59 votes

.readlines() lit toutes les lignes en une seule fois, c'est-à-dire, cela bloque jusqu'à ce que le subprocessus se termine (ferme son extrémité du tuyau). Pour lire en temps réel (s'il n'y a pas de problèmes de mise en mémoire tampon), vous pourriez : for line in iter(p.stdout.readline, ''): print line,

4 votes

Pouvez-vous élaborer sur ce que vous entendez par "s'il n'y a pas de problèmes de mise en mémoire tampon"? Si le processus bloque définitivement, l'appel de sous-processus bloque également. La même chose pourrait arriver avec mon exemple original aussi. Que pourrait-il se passer d'autre en ce qui concerne la mise en mémoire tampon?

20 votes

Le processus enfant peut utiliser le buffering par bloc en mode non interactif au lieu du buffering par ligne donc p.stdout.readline() (note : pas de s à la fin) ne verra pas de données tant que l'enfant n'aura pas rempli son tampon. Si l'enfant ne produit pas beaucoup de données, la sortie ne sera pas en temps réel. Voir la deuxième raison dans Q: Pourquoi ne pas simplement utiliser un tube (popen()) ?. Certains contournements sont fournis dans cette réponse (pexpect, pty, stdbuf)

273voto

newtover Points 12301

Quelques astuces sur comment détacher le processus enfant du processus appelant (démarrer le processus enfant en arrière-plan).

Supposez que vous voulez démarrer une tâche longue à partir d'un script CGI. C'est-à-dire que le processus enfant doit vivre plus longtemps que le processus d'exécution du script CGI.

L'exemple classique de la documentation du module subprocess est le suivant :

import subprocess
import sys

# Du code ici

pid = subprocess.Popen([sys.executable, "longtask.py"]) # Appeler le sous-processus

# Encore un peu de code ici

L'idée ici est que vous ne voulez pas attendre à la ligne 'Appeler le sous-processus' jusqu'à ce que longtask.py ait terminé. Mais il n'est pas clair ce qu'il se passe après la ligne 'encore un peu de code ici' de l'exemple.

Ma plateforme cible était FreeBSD, mais le développement était sur Windows, donc j'ai rencontré le problème sur Windows en premier.

Sous Windows (Windows XP), le processus parent ne se terminera pas tant que 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 est de passer le drapeau de création de processus DETACHED_PROCESS Process Creation Flag à la fonction sous-jacente CreateProcess dans l'API Windows. Si vous avez installé pywin32, vous pouvez importer le drapeau depuis le 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 le drapeau sémantiquement correct est CREATE_NEW_CONSOLE (0x00000010) */

Sous FreeBSD, nous avons un autre problème : lorsque le processus parent se termine, il termine également les processus enfants. Et ce n'est pas ce que vous voulez dans un script CGI non plus. Quelques 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 sous FreeBSD. Si quelqu'un sait, veuillez partager vos idées. Une recherche sur le démarrage de processus en arrière-plan en Python ne semble pas encore éclairer.

0 votes

J'ai remarqué un possible "caprice" lors du développement d'applications py2exe dans pydev+eclipse. J'ai pu constater que le script principal n'était pas détaché car la fenêtre de sortie d'eclipse ne se terminait pas ; même si le script s'exécute complètement, il attend toujours des retours. Cependant, lorsque j'ai essayé de compiler vers un exécutable py2exe, le comportement attendu se produit (exécute les processus en mode détaché, puis se quitte). Je ne suis pas sûr, mais le nom de l'exécutable n'est plus dans la liste des processus. Cela fonctionne pour toutes les approches (os.system("start *"), os.spawnl avec os.P_DETACH, subprocs, etc.).

0 votes

Windows gotcha: même si j'ai lancé le processus avec DETACHED_PROCESS, lorsque j'ai tué mon démon Python, tous les ports ouverts par celui-ci n'étaient pas libérés tant que tous les processus lancés ne se terminaient pas. WScript.Shell a résolu tous mes problèmes. Exemple ici: pastebin.com/xGmuvwSx

3 votes

Vous pourriez également avoir besoin du drapeau CREATE_NEW_PROCESS_GROUP. Voir Popen waiting for child process even when the immediate child has terminated

172voto

SirWart Points 627

Je recommanderais d'utiliser le module subprocess plutôt que os.system car il gère l'échappement du shell pour vous et est donc beaucoup plus sûr.

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

0 votes

Si vous souhaitez créer une liste à partir d'une commande avec des paramètres, une liste qui peut être utilisée avec subprocess lorsque shell=False, utilisez alors shlex.split pour une manière facile de le faire docs.python.org/2/library/shlex.html#shlex.split (c'est la méthode recommandée selon la documentation docs.python.org/2/library/subprocess.html#popen-constructor)

13 votes

Ceci est incorrect : "il échappe au shell pour vous et est donc beaucoup plus sûr". subprocess n'échappe pas au shell, subprocess ne passe pas votre commande à travers le shell, donc il n'est pas nécessaire d'échapper au shell.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X