script - python lancer un programme externe




Appeler une commande externe en Python (20)

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


Appeler 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?

A partir de Python 3.5, la documentation recommande subprocess.run :

L'approche recommandée pour appeler des sous-processus consiste à utiliser la fonction run () pour tous les cas d'utilisation qu'il 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 fonctionne 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 correctement, puis renvoie un objet CompletedProcess . Au lieu de cela, il peut augmenter TimeoutExpired (si vous lui donnez un timeout= argument) ou CalledProcessError (s'il échoue et que vous transmettez check=True ).

Comme vous pouvez le déduire de l'exemple ci-dessus, stdout et stderr sont redirigés vers votre propre stdout et stderr par défaut.

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

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

Capture de sortie

Si vous souhaitez capturer la sortie, vous pouvez transmettre le subprocess.PIPE à la commande stderr ou stdout appropriée:

>>> 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 placées dans stderr au lieu de stdout.)

Passer une liste de commandes

On peut facilement passer d'une chaîne de commande manuellement (comme le suggère la question) à une chaîne générée par programme. Ne construisez pas de chaînes par programmation. C'est un problème de sécurité potentiel. Il est préférable de 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'

Notez que seuls les args doivent être passés 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 popenargs et les kwargs sont donnés au constructeur Popen . input peut être une chaîne d'octets (ou unicode, si vous spécifiez encoding ou universal_newlines=True ) qui sera acheminée vers le stdin du sous-processus.

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

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

Si check est vrai 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, ainsi que stdout et stderr s'ils ont été capturés.

et cet exemple pour check=True est 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 élargie

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 positionnellement. Donc, passez les arguments restants comme arguments de mots clés.

Popen

Quand utiliser Popen place? Je m'efforcerais de trouver un cas d'utilisation basé uniquement sur les arguments. Cependant, l'utilisation directe de Popen vous donnerait accès à ses méthodes, y compris poll , 'send_signal', 'terminate' et 'wait'.

Voici la signature Popen telle qu'elle est donnée dans la source . Je pense que ceci 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 documentation de Popen 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écuter 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 comme un exercice pour le lecteur.


Avec bibliothèque standard

Le module de sous-processus Use (Python 3):

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

C'est la méthode standard recommandée. Cependant, des tâches plus complexes (pipes, 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 manière similaire.

ProTip: docs.python.org/2/library/shlex.html#shlex.split peut vous aider à analyser la commande pour run , call et autres fonctions de subprocess au cas où vous ne voudriez pas (ou que vous ne puissiez pas!) Les fournir sous forme de listes:

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

Avec dépendances externes

Si les dépendances externes ne vous dérangent pas, utilisez plumbum :

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

C'est le meilleur wrapper de subprocess . Il est multi-plateforme, c’est-à-dire qu’il fonctionne à la fois sous Windows et sous Unix. Installer par pip install plumbum .

Une autre bibliothèque populaire est sh :

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

Cependant, sh abandonné le support Windows, ce qui fait qu'il n'est plus aussi génial qu'auparavant. Installer par pip install sh .


C'est comme ça que je lance mes commandes. Ce code contient 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()

Cela peut être aussi simple:

import os
cmd = "your command"
os.system(cmd)

Il y a beaucoup de bibliothèques différentes qui vous permettent d'appeler des commandes externes avec Python. Pour chaque bibliothèque, j'ai donné une description et 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 souhaitez en savoir plus sur l’une des bibliothèques que j’ai énumérées et reliée à la documentation correspondante.

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 / d'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 "la fonctionnalité dépendant du système d'exploitation". Il peut également être utilisé pour appeler des commandes externes avec os.system et os.popen (Remarque: il existe également un sous-processus.popen). os exécutera toujours le shell et constitue 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'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

plombier

Plumbum est une bibliothèque pour les programmes Python "de type script". Vous pouvez appeler des programmes tels que 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

attendre

pexpect vous permet de créer 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 [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 l'exécution de 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 appelé "sous-processus pour les humains". Il est utilisé comme un wrapper de commodité autour du module de subprocess - subprocess .

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

commandes

os.popen commands contient des fonctions d' os.popen pour os.popen , mais elle a été supprimée de Python 3 car le subprocess - subprocess constitue une meilleure alternative.

Le montage était basé sur le commentaire de JF Sebastian.


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

subprocess.Popen exécute la <commande> en tant que sous-processus. Dans mon cas, je dois 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 à partir du terminal.

Un de plus: (NOTE: 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 de programme se bloque jusqu'à ce que l'utilisateur ferme kwrite. Pour surmonter cela, j'ai essayé à la place os.system(konsole -e kwrite). Cette fois, le programme a continué de circuler, mais kwrite est devenu le sous-processus de la console.

N'importe qui exécute kwrite qui n'est pas un sous-processus (c'est-à-dire qu'il doit apparaître dans le moniteur système à l'extrême gauche de l'arborescence).


J'utilise typiquement:

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


Je recommanderais d'utiliser le module subprocess au lieu de os.system car il génère un échappement de shell pour vous et est donc beaucoup plus sûr: http://docs.python.org/library/subprocess.html

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

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

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

L'avantage du sous - processus par rapport au système est qu'il est plus flexible (vous pouvez obtenir le stdout, le stderr, le code "réel", une meilleure gestion des erreurs, etc.).

La documentation officielle recommande le module de sous - processus plutôt que l'option os.system ():

Le module de sous-processus offre des fonctionnalités plus puissantes pour générer de nouveaux processus et extraire 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 avec le module de sous-processus " de la documentation de sous - processus peut contenir des recettes utiles.

Documentation officielle sur le module de sous - processus :


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

Utilisation:

import os

cmd = 'ls -al'

os.system(cmd)

os - Ce module fournit un moyen portable d'utiliser des fonctionnalités dépendant du système d'exploitation.

Pour la os fonctions os , here la documentation.


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

Il permet le contrôle interactif de 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')


os.system est OK, mais un peu daté. Ce n'est pas très sécurisé non plus. Au lieu de cela, essayez le subprocess . subprocess - subprocess n'appelle pas sh directement et est donc plus sécurisé que os.system .

Obtenez plus d'informations here .


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


Vous pouvez utiliser Popen, puis 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 .


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

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

J'aime bien shell_command pour sa simplicité. Il est construit sur le 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

Sous Linux, si vous souhaitez 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 spooler de tâches ou la commande at

Un exemple avec le spouleur de tâches:

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

Remarques sur le spooler 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 des sources en un simple make, l'ajouter à votre chemin et vous avez terminé.


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 déconseillé depuis la version 2.6 au profit du module de sous - processus , ce que d'autres réponses ont bien couvert.





external