Appel d'une commande externe à partir de Python

4886

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?

FreshWoWer
la source

Réponses:

4696

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

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

L'avantage de subprocessrapport systemest qu'il est plus souple (vous pouvez obtenir le stdout, stderr, le code d'état « réel », une meilleure gestion des erreurs, etc ...).

La documentation officielle recommande le subprocessmodule plutôt que l'alternative os.system():

Le subprocessmodule offre 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()].

La section Remplacement de fonctions plus anciennes par la section Module de sous-processus de la subprocessdocumentation peut contenir des recettes utiles.

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

import subprocess
subprocess.call(["ls", "-l"])
David Cournapeau
la source
Existe-t-il un moyen d'utiliser la substitution de variables? IE J'ai essayé de le faire echo $PATHen utilisant call(["echo", "$PATH"]), mais cela faisait juste écho à la chaîne littérale $PATHau lieu de faire une substitution. Je sais que je pourrais obtenir la variable d'environnement PATH, mais je me demande s'il existe un moyen facile de faire en sorte que la commande se comporte exactement comme si je l'avais exécutée dans bash.
Kevin Wheeler du
@KevinWheeler Vous devrez utiliser shell=Truepour que cela fonctionne.
SethMMorton
39
@KevinWheeler Vous ne devez PAS utiliser shell=True, à cet effet Python est fourni avec 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
Murmel
appel bloque-t-il? c'est-à-dire si je veux exécuter plusieurs commandes dans une forboucle, comment puis-je le faire sans qu'il bloque mon script python? Je me fiche de la sortie de la commande, je veux juste en exécuter beaucoup.
Charlie Parker,
5
Si vous voulez créer une liste à partir d'une commande avec des paramètres , une liste qui peut être utilisée avec subprocessquand shell=False, alors utilisez shlex.splitpour un moyen facile de le faire docs.python.org/2/library/shlex.html#shlex.split
Daniel F
2985

Voici un résumé des façons d'appeler des programmes externes et les avantages et inconvénients de chacun:

  1. os.system("some_command with args")transmet la commande et les arguments au shell de votre système. C'est bien parce que vous pouvez réellement exécuter plusieurs commandes à la fois de cette manière et configurer des canaux et une redirection d'entrée / sortie. Par exemple:

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

Cependant, bien que cela soit pratique, vous devez gérer manuellement l'échappement des caractères 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 des programmes réellement externes. Consultez la documentation .

  1. stream = os.popen("some_command with args")fera la même chose que, os.systemsauf qu'il vous donne un objet de type fichier que vous pouvez utiliser pour accéder à l'entrée / sortie standard pour ce processus. Il existe 3 autres variantes de popen qui gèrent toutes les E / S légèrement différemment. Si vous passez tout sous forme de chaîne, votre commande est transmise au shell; si vous les passez sous forme de liste, vous n'avez pas à vous soucier de ne rien échapper. Consultez la documentation .

  2. La Popenclasse du subprocessmodule. Ceci est destiné à remplacer, os.popenmais présente l'inconvénient d'être légèrement plus compliqué en raison de son caractère complet. 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 dans une classe unifiée au lieu de 4 fonctions popen différentes. Consultez la documentation .

  3. La callfonction du subprocessmodule. C'est fondamentalement comme la Popenclasse et prend tous les mêmes arguments, mais il attend simplement que la commande se termine et vous donne le code retour. Par exemple:

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

    Consultez la documentation .

  4. Si vous utilisez Python 3.5 ou une version ultérieure, vous pouvez utiliser la nouvelle subprocess.runfonction, qui ressemble beaucoup à la précédente, mais encore plus flexible et renvoie un CompletedProcessobjet lorsque la commande a terminé son exécution.

  5. Le module os a également toutes les fonctions fork / exec / spawn que vous auriez dans un programme C, mais je ne recommande pas de les utiliser directement.

Le subprocessmodule devrait probablement être ce que vous utilisez.

Enfin, sachez que pour toutes les méthodes où vous passez la commande finale à exécuter par le shell sous forme de chaîne et que vous êtes responsable de l'échapper. Il y a de sérieuses implications pour la sécurité si une partie de la chaîne que vous passez ne peut pas être entièrement approuvée. Par exemple, si un utilisateur entre une partie ou une partie de la chaîne. En cas de doute, utilisez uniquement ces méthodes avec des constantes. Pour vous donner un aperçu 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'aime pas && rm -rf /" qui pourrait effacer tout le système de fichiers.

Eli Courtwright
la source
22
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. La devise la plus connue de Perl est" Il y a plus d'une façon de le faire ". Python est conçu pour avoir une façon évidente de le faire" On dirait que cela devrait être l'autre côté! En Perl, je ne connais que deux façons d'exécuter une commande - en utilisant back-tick ou open.
Jean
12
Si vous utilisez Python 3.5+, utilisez subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run
phoenix
4
Ce que l'on doit généralement savoir, c'est ce qui est fait avec les processus STDOUT et STDERR du processus enfant, car s'ils sont ignorés, dans certaines conditions (assez courantes), le processus enfant émettra éventuellement un appel système pour écrire dans STDOUT (STDERR aussi?) cela 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 que certains processus lisent à partir de ce tampon. Donc, avec les méthodes actuellement recommandées subprocess.run(..), que fait exactement "Cela ne capture pas stdout ou stderr par défaut". impliquer? Et subprocess.check_output(..)STDERR?
Evgeni Sergeev
2
@Pitto oui, mais ce n'est pas ce qui est exécuté par l'exemple. Remarquez le echodevant de la chaîne passée à Popen? Ainsi, la commande complète sera echo my mama didnt love me && rm -rf /.
Chris Arndt
6
C'est sans doute le mauvais sens. La plupart des gens n'ont besoin que de subprocess.run()leurs frères subprocess.check_call()et sœurs plus âgés et al. Pour les cas où ceux-ci ne suffisent pas, voir subprocess.Popen(). os.popen()ne devrait peut-être pas être mentionné du tout, ou même après "pirater votre propre code fork / exec / spawn".
tripleee du
358

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 stdoutdonnées du tube. En fait, vous pouvez simplement omettre ces paramètres ( stdout=et stderr=) et cela se comportera comme os.system().

EmmEff
la source
44
.readlines()lit toutes les lignes à la fois, c'est-à-dire qu'il bloque jusqu'à ce que le sous-processus se termine (ferme son extrémité du tuyau). Pour lire en temps réel (s'il n'y a pas de problème de mise en mémoire tampon), vous pouvez:for line in iter(p.stdout.readline, ''): print line,
jfs
1
Pourriez-vous préciser ce que vous entendez par «s'il n'y a pas de problème de mise en mémoire tampon»? Si le processus bloque définitivement, l'appel de sous-processus bloque également. La même chose pourrait se produire avec mon exemple d'origine également. Que pourrait-il arriver d'autre en ce qui concerne la mise en mémoire tampon?
EmmEff
15
le processus enfant peut utiliser la mise en mémoire tampon de bloc en mode non interactif au lieu de la mise en mémoire tampon de ligne afin p.stdout.readline()(note: non sà la fin) ne verra aucune donnée jusqu'à ce que l'enfant remplisse sa mémoire 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 tuyau (popen ())? . Certaines solutions de contournement sont fournies dans cette réponse (pexpect, pty, stdbuf)
jfs
4
le problème de mise en mémoire tampon n'a d'importance que si vous voulez une sortie en temps réel et ne s'applique pas à votre code qui n'imprime rien tant que toutes les données ne sont pas reçues
jfs
3
Cette réponse était bien pour son temps, mais nous ne devrions plus recommander Popenpour des tâches simples. Cela spécifie également inutilement shell=True. Essayez l'une des subprocess.run()réponses.
tripleee
230

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 il n'est pas clair 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 l'indicateur de création de processus DETACHED_PROCESS à la fonction CreateProcess sous-jacente dans l'API Windows. Si vous avez installé pywin32, vous pouvez importer l'indicateur à partir 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, veuillez partager vos idées. Googler sur le démarrage de processus d'arrière-plan en Python ne fait pas encore la lumière.

newtover
la source
j'ai remarqué une "bizarrerie" possible avec le développement d'applications py2exe dans pydev + eclipse. j'ai pu dire 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 jusqu'à son terme, il attend toujours des retours. mais, lorsque j'ai essayé de compiler en un exécutable py2exe, le comportement attendu se produit (exécute les processus en tant que détaché, puis se ferme). 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.)
maranas
1
vous pourriez également avoir besoin de l'indicateur CREATE_NEW_PROCESS_GROUP. Voir Popen en attente du processus enfant même lorsque l'enfant immédiat est terminé
jfs
5
Ce qui suit est incorrect: "[o] n fenêtres (win xp), le processus parent ne se terminera pas tant que le longtask.py n'aura pas terminé son travail". Le parent se fermera normalement, mais la fenêtre de la console (instance conhost.exe) ne se ferme que lorsque le dernier processus attaché se termine, et l'enfant peut avoir hérité de la console du parent. Réglage DETACHED_PROCESSen creationflagsévite en empêchant l'enfant d'hériter ou de la création d' une console. Si vous souhaitez plutôt une nouvelle console, utilisez CREATE_NEW_CONSOLE(0x00000010).
Eryk Sun
1
Je ne voulais pas dire que l'exécution en tant que processus détaché est incorrecte. Cela dit, vous devrez peut-être définir les poignées standard sur les fichiers, les canaux ou os.devnullparce que certains programmes de console se terminent avec une erreur sinon. Créez une nouvelle console lorsque vous souhaitez que le processus enfant interagisse avec l'utilisateur simultanément avec le processus parent. Il serait déroutant d'essayer de faire les deux dans une seule fenêtre.
Eryk Sun
1
n'y a-t-il pas un moyen indépendant du système d'exploitation pour que le processus s'exécute en arrière-plan?
Charlie Parker
152
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.

nimish
la source
6
Je ne sais pas ce que je voulais dire il y a près d'une décennie (vérifiez la date!), Mais si je devais deviner, ce serait qu'aucune validation ne serait effectuée.
nimish
1
Cela devrait maintenant être considéré subprocesscomme une solution légèrement plus polyvalente et portable. L'exécution de commandes externes est bien sûr intrinsèquement non transférable (vous devez vous assurer que la commande est disponible sur toutes les architectures que vous devez prendre en charge) et passer des entrées utilisateur en tant que commande externe est intrinsèquement dangereux.
tripleee
1
Notez l'horodatage de ce type: la "bonne" réponse a 40x les votes et est la réponse # 1.
nimish
La seule solution qui a fonctionné pour moi pour exécuter des trucs NodeJS.
Nikolay Shindarov
148

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

subprocess.call(['ping', 'localhost'])
sirwart
la source
Si vous voulez créer une liste à partir d'une commande avec des paramètres , une liste qui peut être utilisée avec subprocessquand shell=False, alors utilisez shlex.splitpour un moyen facile de le faire docs.python.org/2/library/shlex.html#shlex.split ( c'est la manière recommandée selon les documents docs.python.org/2/library/subprocess.html#popen-constructor )
Daniel F
6
Ceci est incorrect: " il échappe au shell pour vous et est donc beaucoup plus sûr ". le sous-processus n'échappe pas au shell, le sous-processus ne passe pas votre commande à travers le shell, il n'est donc pas nécessaire de l'échapper.
Lie Ryan
144
import os
cmd = 'ls -al'
os.system(cmd)

Si vous souhaitez renvoyer les résultats de la commande, vous pouvez utiliser os.popen. 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.

Alexandra Franks
la source
10
popen est déconseillé au profit du sous - processus .
Fox Wilson
Vous pouvez également enregistrer votre résultat avec l'appel os.system, car il fonctionne comme le shell UNIX lui-même, comme par exemple os.system ('ls -l> test2.txt')
Stefan Gruenwald
97

Il existe de nombreuses bibliothèques différentes qui vous permettent d'appeler des commandes externes avec Python. Pour chaque bibliothèque, j'ai donné une description et montré un exemple d'appel d'une commande externe. La commande que j'ai utilisée comme exemple est ls -l(lister tous les fichiers). Si vous souhaitez en savoir plus sur l'une des bibliothèques que j'ai répertoriées et lié la documentation de chacune d'entre elles.

Sources:

Ce sont toutes les bibliothèques:

J'espère que cela vous aidera à prendre une décision sur la bibliothèque à utiliser :)

sous-processus

Le sous-processus vous permet d'appeler des commandes externes et de les connecter à leurs canaux d'entrée / sortie / erreur (stdin, stdout et stderr). Le sous-processus est le choix par défaut pour exécuter des commandes, mais parfois d'autres modules sont meilleurs.

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 est utilisé pour les "fonctionnalités dépendantes du système d'exploitation". Il peut également être utilisé pour appeler des commandes externes avec os.systemet os.popen(Remarque: il existe également un sous-processus.popen). os exécutera toujours le shell et est une alternative simple pour les personnes qui n'en ont pas besoin ou ne savent pas comment l'utiliser subprocess.run.

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

sh

sh est une interface de sous-processus qui vous permet d'appeler des programmes comme s'il s'agissait de fonctions. Ceci est utile si vous souhaitez exécuter une commande plusieurs fois.

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

plumbum

plumbum est une bibliothèque de programmes Python "de type script". Vous pouvez appeler des programmes comme des fonctions comme dans sh. Plumbum est utile si vous souhaitez exécuter un pipeline sans le shell.

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

pexpect

pexpect vous permet de générer des applications enfants, de les contrôler et de trouver des modèles dans leur sortie. C'est une meilleure alternative au sous-processus pour les commandes qui attendent un tty sur 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')

en tissu

fabric est une bibliothèque Python 2.5 et 2.7. Il vous permet d'exécuter des commandes shell locales et distantes. Fabric est une alternative simple pour exécuter des commandes dans un shell sécurisé (SSH)

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

envoyé

envoyé est connu comme "sous-processus pour les humains". Il est utilisé comme un emballage pratique autour du subprocessmodule.

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

commandes

commandscontient des fonctions wrapper pour os.popen, mais il a été supprimé de Python 3 car subprocessc'est une meilleure alternative.

L'édition était basée sur le commentaire de JF Sebastian.

Tom Fuller
la source
74

J'utilise toujours fabricpour cela des 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)
Jorge E. Cardona
la source
73

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')
athanassis
la source
70

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 (tuyaux, sortie, entrée, etc.) peuvent être fastidieuses à construire et à écrire.

Remarque sur la version Python: Si vous utilisez toujours Python 2, subprocess.call fonctionne de la même manière.

ProTip: shlex.split peut vous aider à analyser la commande pour run, callet d'autres subprocessfonctions au cas où vous ne voudriez pas (ou si 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 les dépendances externes, utilisez plumbum :

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

C'est le meilleur subprocessemballage. Il est multiplateforme, c'est-à-dire qu'il fonctionne à la fois sur les systèmes Windows et Unix. Installer par pip install plumbum.

Une autre bibliothèque populaire est sh :

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

Cependant, la shprise en charge de Windows a été supprimée, donc ce n'est plus aussi génial qu'avant. Installer par pip install sh.

Honza Javorek
la source
69

Si vous avez besoin de la sortie de la commande que vous appelez, vous pouvez 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 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'expansion de ~ au domicile d'un utilisateur annuaire. Toutefois, notez que Python lui - même offre des implémentations de plusieurs coquille caractéristiques (en particulier glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser()et shutil).

Facundo Casco
la source
1
Notez que check_outputnécessite une liste plutôt qu'une chaîne. Si vous ne comptez pas sur les espaces entre guillemets pour valider votre appel, la façon la plus simple et la plus lisible de le faire est subprocess.check_output("ls -l /dev/null".split()).
Bruno Bronosky
56

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()
Usman Khan
la source
3
Je pense que c'est acceptable pour les commandes codées en dur, si cela augmente la lisibilité.
Adam Matan
54

Mise à jour:

subprocess.runest 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 pas aussi simple cependant. 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 fonctions. Envoy est un sous-processus pour les humains.

Exemple d'utilisation du 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'>]
Joe
la source
43

Utilisez un sous-processus .

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

import os
os.system('cat testfile')
Ben Hoffstein
la source
36

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

Obtenez plus d'informations ici .

Martin W
la source
2
Bien que je sois d'accord avec la recommandation générale, subprocessne supprime pas tous les problèmes de sécurité et présente certains problèmes embêtants.
tripleee
36

Appel d'une commande externe en Python

Simple, utilisation subprocess.run, qui renvoie un CompletedProcessobjet:

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

runattend que la commande se termine avec succès, puis retourne un CompletedProcessobjet. Il peut à la place augmenter TimeoutExpired(si vous lui donnez un timeout=argument) 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à la appropriée stderrou stdout:

>>> 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 pourrait 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 construisez 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 argsdoit être passé positionnellement.

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 popenargset kwargssont donnés au Popenconstructeur.inputpeut être une chaîne d'octets (ou unicode, si vous spécifiez l'encodage ou universal_newlines=True) qui sera redirigée vers le stdin du sous-processus.

La documentation décrit timeout=etcheck=True mieux que je ne pouvais:

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 check=Trueest meilleur que celui que je pourrais 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 des arguments doit être passée de manière positionnelle. Passez donc les arguments restants comme arguments de mots clés.

Popen

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

Voici la Popensignature 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 la Popendocumentation est plus informative :

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 Popensera laissé comme un exercice pour le lecteur.

Aaron Hall
la source
Un exemple simple de communication bidirectionnelle entre un processus principal et un sous-processus peut être trouvé ici: stackoverflow.com/a/52841475/1349673
James Hirschorn
Le premier exemple devrait probablement avoir shell=Trueou (mieux encore) passer la commande sous forme de liste.
tripleee
33

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
stuckintheshuck
la source
28

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 plus de osfonctions, voici la documentation.

Priyankara
la source
2
il est également obsolète. utiliser le sous
Corey Goldberg
28

Cela peut être aussi simple:

import os
cmd = "your command"
os.system(cmd)
Samadi Salahedine
la source
1
Cela ne met pas en évidence les inconvénients, qui sont expliqués de manière beaucoup plus détaillée dans PEP-324 . La documentation de os.systemrecommande explicitement de l'éviter en faveur de subprocess.
tripleee
25

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
mdwhatcott
la source
24

Il y a une autre différence ici qui n'est pas mentionnée précédemment.

subprocess.Popenexécute la <commande> en tant que sous-processus. Dans mon cas, j'ai besoin d'exécuter le fichier <a> qui doit communiquer avec un autre programme, <b>.

J'ai essayé le sous-processus et l'exécution a réussi. Cependant, <b> n'a pas pu communiquer avec <a>. Tout est normal lorsque je lance les deux depuis le terminal.

Un de plus: (REMARQUE: kwrite se comporte différemment des autres applications. Si vous essayez ce qui suit avec Firefox, les résultats ne seront pas les mêmes.)

Si vous essayez os.system("kwrite"), le flux du programme se bloque jusqu'à ce que l'utilisateur ferme kwrite. Pour surmonter cela, j'ai plutôt essayé os.system(konsole -e kwrite). Ce programme horaire a continué de couler, mais kwrite est devenu le sous-processus de la console.

N'importe qui exécute le kwrite n'étant pas un sous-processus (c'est-à-dire que dans le moniteur du système, il doit apparaître au bord le plus à gauche de l'arborescence).

Atinc Delican
la source
1
Qu'entendez-vous par «N'importe qui exécute le kwrite n'étant pas un sous-processus» ?
Peter Mortensen
23

os.systemne vous permet pas de stocker les résultats, donc si vous voulez stocker les résultats dans une liste ou quelque chose, ça subprocess.callfonctionne.

Saurabh Bangad
la source
22

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

cdunn2001
la source
22

J'ai tendance à utiliser un 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)
Emil Stenström
la source
17

Plug sans vergogne, j'ai écrit une bibliothèque pour cela: 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}'"
houqp
la source
16

Sous Windows, vous pouvez simplement importer le subprocessmodule 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
Swadhikar C
la source
15

Sous Linux, au cas où vous voudriez appeler une commande externe qui s'exécutera indépendamment (continuera à s'exécuter après la fin du script python), vous pouvez utiliser une simple file d'attente comme spouleur de tâches ou at commande

Un exemple avec le spouleur de tâches:

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

Remarques sur le spouleur de tâches ( ts):

  1. Vous pouvez définir le nombre de processus simultanés à exécuter ("slots") avec:

    ts -S <number-of-slots>

  2. L'installation tsne nécessite pas de privilèges d'administrateur. Vous pouvez le télécharger et le compiler à partir de la source avec un simple make, l'ajouter à votre chemin et vous avez terminé.

Yuval Atzmon
la source
1
tsn'est pas standard sur aucune distribution que je connaisse, bien que le pointeur vers atsoit légèrement utile. Vous devriez probablement aussi mentionner batch. Comme ailleurs, la os.system()recommandation devrait probablement mentionner au moins qu'il subprocesss'agit de son remplacement recommandé.
tripleee
15

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

Découvrez subprocess.Popen .

admirer
la source
15

Pour récupérer l'ID réseau à partir du neutron OpenStack :

#!/usr/bin/python
import os
netid = "nova net-list | awk '/ External / { print $2 }'"
temp = os.popen(netid).read()  /* Here temp also contains new line (\n) */
networkId = temp.rstrip()
print(networkId)

Sortie de nova net-list

+--------------------------------------+------------+------+
| ID                                   | Label      | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1      | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External   | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal   | None |
+--------------------------------------+------------+------+

Sortie d' impression (networkId)

27a74fcd-37c0-4789-9414-9531b7e3f126
IRSHAD
la source
Vous ne devriez pas le recommander os.popen()en 2016. Le script Awk pourrait facilement être remplacé par du code Python natif.
tripleee