Question Appel d'une commande externe en Python


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


3622
2017-09-18 01:35


origine


Réponses:


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

from subprocess import call
call(["ls", "-l"])

L'avantage de sous-processus contre. système c'est qu'il est plus flexible (vous pouvez obtenir le stdout, le stderr, le "vrai" code d'état, une meilleure gestion des erreurs, etc ...).

le documentation officielle recommande le sous-processus module sur l'alternative os.system ():

le sous-processus le module fournit des installations plus puissantes pour engendrer de nouveaux processus et récupérer leurs résultats; utiliser ce module est préférable à l'utilisation de cette fonction [os.system()]

Le "Remplacer les anciennes fonctions avec le module de sous-processus"section dans le sous-processus la documentation peut avoir des recettes utiles.

Documentation officielle sur le sous-processus module:


3491
2017-09-18 01:39



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

  1. os.system("some_command with args") passe 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 les tuyaux et la 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'un autre côté, cela vous permet également d'exécuter des commandes qui sont simplement des commandes shell et non des programmes externes. Voir La documentation.

  2. stream = os.popen("some_command with args") fera la même chose que os.system sauf qu'il vous donne un objet semblable à un fichier que vous pouvez utiliser pour accéder à l'entrée / sortie standard pour ce processus. Il y a 3 autres variantes de popen qui gèrent toutes les E / S légèrement différemment. Si vous passez tout en chaîne, votre commande est transmise au shell. Si vous les passez en liste, vous n'avez pas besoin de vous soucier d'échapper à quoi que ce soit. Voir La documentation.

  3. le Popen classe de subprocess module. Ceci est destiné à remplacer os.popen mais a l'inconvénient d'être un peu plus compliqué en raison d'être si 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. Voir La documentation.

  4. le call fonction de la subprocess module. C'est fondamentalement comme le Popen class 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)  
    

    Voir La documentation.

  5. Si vous utilisez Python 3.5 ou version ultérieure, vous pouvez utiliser le nouveau subprocess.run fonction, qui est un peu comme ci-dessus, mais encore plus flexible et renvoie un CompletedProcess objet lorsque la commande termine l'exécution.

  6. 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 subprocess module devrait probablement être ce que vous utilisez.

Enfin, sachez que pour toutes les méthodes où vous passez la dernière commande à exécuter par le shell sous forme de chaîne, vous êtes responsable de son échappement. Il y a de graves implications pour la sécurité si une partie de la chaîne que vous transmettez ne peut pas être entièrement approuvée. Par exemple, si un utilisateur entre une partie / 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 "ma maman ne m'aime pas && rm -rf /".


2466
2017-09-18 13:11



J'utilise généralement:

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 le stdout les données dans le tuyau. En fait, vous pouvez simplement omettre ces paramètres (stdout= et stderr=) et ça va se comporter comme os.system().


255
2017-09-18 18:20



Quelques astuces pour détacher le processus fils de l'appelant (en démarrant le processus fils en arrière-plan).

Supposons que vous souhaitiez démarrer une tâche longue à partir d'un script CGI, c'est-à-dire que le processus enfant doit durer plus longtemps que le processus d'exécution du script CGI.

L'exemple classique du document de 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 'appel subprocess' jusqu'à ce que le longtask.py soit fini. 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 fait face au problème sur windows.

Sous Windows (win xp), le processus parent ne se termine pas tant que le fichier longtask.py n'a pas terminé son travail. Ce n'est pas ce que vous voulez dans CGI-script. 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 DETACHED_PROCESS Drapeau de création de processus à la fonction CreateProcess sous-jacente dans l'API Win. 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 notes, 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 fils. Et ce n'est pas ce que vous voulez dans le script CGI non plus. Certaines expériences ont montré que le problème semblait être de partager 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. Googling sur le démarrage des processus d'arrière-plan en Python ne fait pas encore de lumière.


157
2018-02-12 10:15



Je vous recommande d'utiliser le module de sous-processus à la place de os.system car il évite le shell pour vous et est donc beaucoup plus sûr: http://docs.python.org/library/subprocess.html

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

97
2017-09-18 01:42



import os
cmd = 'ls -al'
os.system(cmd)

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


93
2017-09-18 01:37



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 consulter la documentation pertinente sur les modules 'os' et 'sys'. Il y a un tas de fonctions (exec * et spawn *) qui feront des choses similaires.


82
2017-09-18 01:37



J'utilise toujours fabric pour ce genre de choses:

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

Regarde un exemple:

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

51
2018-03-13 00:12



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

Il permet le 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')

51
2017-10-07 07:09



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

Sources:

Ce sont toutes les bibliothèques:

J'espère que cela vous aidera à décider quelle 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 l'exécution 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 "fonctionnalité dépendante du système d'exploitation". Il peut également être utilisé pour appeler des commandes externes avec os.system et os.popen (Note: Il y a aussi un sous-processus.popen). os fonctionnera toujours la coque et est une alternative simple pour les personnes qui n'en ont pas besoin ou qui ne savent pas comment 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'ils étaient des fonctions. C'est utile si vous voulez lancer 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 "script-like". Vous pouvez appeler des programmes comme des fonctions comme dans sh. Plumbum est utile si vous voulez 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 sous Unix.

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

en tissu

le tissu 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 de commodité autour de la subprocess module.

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

commandes

commands contient des fonctions d'emballage pour os.popen, mais il a été retiré de Python 3 depuis subprocess est une meilleure alternative.

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


46
2017-10-29 14:02



Si vous avez besoin de la sortie de la commande que vous appelez, alors 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 coquille paramètre.

Si la coquille 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 système et que vous souhaitez accéder aux autres fonctionnalités du shell telles que shell pipes, wildcards de noms de fichiers, extension de variables d'environnement et extension annuaire. Cependant, notez que Python lui-même offre des implémentations de nombreuses fonctionnalités de type shell (en particulier, glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser(), et shutil).


45
2018-04-28 20:29