Appeler une commande externe depuis Python

Appeler une commande externe depuis Python

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?

Montrez la meilleure réponse

Avant de publier une nouvelle réponse, considérez qu'il y a déjà plus de 60 réponses à cette question. Veuillez vous assurer que votre réponse contient des informations qui ne figurent pas parmi les réponses existantes.

Avant d'utiliser subprocess ou os.system, envisagez de rechercher un package python qui fait la même chose. Il existe généralement des wrappers sur les utilitaires bash couramment utilisés que vous pouvez utiliser.

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 y a un tas de fonctions (exec * et spawn *) qui feront des choses similaires.

Aucune idée de 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'il n'y a pas de validation faite.

Cela devrait maintenant pointer vers subprocess comme une solution légèrement plus polyvalente et portable. L'exécution de commandes externes est bien sûr intrinsèquement non portable (vous devez vous assurer que la commande est disponible sur toutes les architectures que vous devez prendre en charge) et le passage d'une entrée utilisateur en tant que commande externe est intrinsèquement dangereux.

Notez l'horodatage sur ce gars: la réponse "correcte" a 40x les votes et est la réponse n ° 1.

La seule solution qui a fonctionné pour moi pour exécuter des trucs NodeJS.

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 , que d'autres réponses ont bien couvertes.

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

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 code de statut "réel", meilleure gestion des erreurs, etc ...).

La documentation officielle recommande le subprocess module sur le os.system() alternatif:

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 des anciennes fonctions par La section Module de sous-processus de 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"])
 

Existe-t-il un moyen d'utiliser la substitution de variables? IE J'ai essayé de faire echo $PATH en utilisant call(["echo", "$PATH"]) , mais cela faisait juste écho à la chaîne littérale $PATH au 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 simple de faire en sorte que la commande se comporte exactement comme si je l'avais exécutée en bash.

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

@KevinWheeler Vous ne devez PAS utiliser shell=True , pour cela Python est livré 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 = Vrai

L'exemple appelle ls -l mais ne donne pas accès à sa sortie (stdout n'est pas accessible). Je trouve cela déroutant - vous pouvez utiliser une commande sans stdout à la place, telle que touch .

Semble être assez maladroit sur Python3 + Windows . Si j'entre un nom de fichier avec des caractères spéciaux comme & , il lancera un FileNotFoundError . Même si le fichier se trouve dans le répertoire de travail où j'ai exécuté python, et qu'il existe évidemment.

bloque-t-il l'appel? c'est-à-dire si je veux exécuter plusieurs commandes dans une boucle for , comment puis-je le faire sans que cela ne bloque mon script python? Je me fiche de la sortie de la commande, je veux juste en exécuter beaucoup.

Je comprends l'utilisation de call pour des fonctionnalités plus avancées, mais je ne vois rien de mal à utiliser system si cela répond à vos besoins.

Pour simplifier au moins conceptuellement: \ n call ("ls -l" .split ())

Si vous voulez créer une liste à partir d'une commande avec des paramètres , une liste qui peut être utilisée avec subprocess quand shell=False , alors utilisez shlex.split pour un moyen simple de le faire docs.python.org/ 2 / library / shlex.html # shlex.split

subprocess vous permet également de diriger directement deux commandes ensemble, il y a un exemple dans la documentation.

@CharlieParker: blocs call , check_call , check_output et run . Si vous ne souhaitez pas bloquer, utilisez subprocess.Popen .

@sudo: c'est l'inverse. system() est plus avancé que Popen , il ajoute une coquille friggin qui est prête à vous visser quand vous vous y attendez le moins. Quelle coquille? Cela dépend de ce que l'utilisateur a comme envoi SHELL, ce qui signifie que la syntaxe exacte qu'il va vous visser est souvent hors de votre contrôle. Popen est de niveau inférieur et est plus proche de ce à quoi ressemble l'API du système d'exploitation sous-jacent, et a un comportement beaucoup moins surprenant.

@LieRyan J'utiliserais Popen si la syntaxe exacte était quelque chose à craindre, mais si vous exécutez simplement un programme (peut-être avec quelques arguments constants), system ne l'est pas un problème. Aussi, parfois je veux le shell.

vous avez oublié de dire qu'il a besoin au moins de python 3.5. Cela ne fonctionne pas sur python 3.4.3 par exemple, qui est par défaut pour Ubuntu 14.04 LTS

Comme déjà mentionné, cela n'est pris en charge que dans les versions récentes de python. Je pense que cela devrait être indiqué dans la réponse

La réponse originale avait subprocess.call() , donc la personne qui a mis à jour la réponse (et non le répondant d'origine!) Devrait également expliquer ce changement plus en détail.

Vous pouvez également passer sous forme de chaîne au lieu d'une liste de chaînes: subprocess.run("ls -l")

S'ils veulent le nommer run au lieu de call pourquoi ne pas simplement en faire un alias? Certains types de rétrocompatibilité sont difficiles et permettent aux modèles sujets aux erreurs de continuer, mais la création de run == call est facile, simple et presque gratuite. Pourquoi call pour les versions de Python antérieures à 3.5 au lieu des nouvelles et des anciennes versions?

@SamuelMuldoon Le problème est que subprocess.call a une sémantique complètement différente de celle de subprocess.run . subprocess.call existe toujours en Python 3.5–3.8 (pour la rétrocompatibilité), c'est juste que si vous l'avez disponible, subprocess.run est mieux de loin.

Vous pouvez également utiliser os.popen . stackoverflow.com/a/59050139/9789097

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

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

Si vous voulez créer une liste à partir d'une commande avec des paramètres , une liste qui peut être utilisée avec subprocess quand shell=False , alors utilisez shlex.split pour un moyen simple 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 )

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

Utilisez le sous-processus .

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

import os
os.system('cat testfile')
 

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 directement sh et est donc plus sécurisé que os.system .

Obtenez plus d'informations ici .

Bien que je sois d'accord avec la recommandation générale, subprocess ne supprime pas tous les problèmes de sécurité et présente ses propres problèmes.

Voici un résumé des façons d'appeler des programmes externes et des 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 car vous pouvez réellement exécuter plusieurs commandes à la fois de cette manière et configurer des tubes 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'un autre côté, cela vous permet également d'exécuter des commandes qui sont simplement des commandes shell et non des programmes externes. Consultez la documentation .

  1. 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 existe 3 autres variantes de popen qui gèrent toutes les entrées / sorties 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 d'échapper à quoi que ce soit. Consultez la documentation .

  2. La classe Popen du module subprocess . 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 c'est bien d'avoir toutes les options dans une seule classe unifiée au lieu de 4 fonctions popen différentes. Consultez la documentation .

  3. La fonction call du module subprocess . C'est fondamentalement 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)  
     

    Voir la documentation .

  4. Si vous utilisez Python 3.5 ou une version ultérieure, vous pouvez utiliser le nouveau subprocess.run , qui ressemble beaucoup à ce qui précède mais encore plus flexible et renvoie une CompletedProcess objet lorsque la commande a fini de s'exécuter.

  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 module subprocess devrait probablement être celui 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, 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 transmettez ne peut pas être entièrement fiable. Par exemple, si un utilisateur entre une partie / une partie de la chaîne. Si vous n'êtes pas sûr, n'utilisez ces méthodes qu'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'aime pas && rm -rf /" qui pourrait effacer tout le système de fichiers.

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

Ce qu'il faut généralement savoir, c'est ce qui est fait avec STDOUT et STDERR du processus enfant, car s'ils sont ignorés, dans certaines conditions (assez courantes), le processus enfant finira par émettre 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 qu'un processus lise à partir de ce tampon. Donc, avec les méthodes actuellement recommandées, subprocess.run(..) , que signifie exactement "Cela ne capture pas stdout ou stderr par défaut." ? Qu'en est-il de subprocess.check_output(..) et STDERR?

laquelle des commandes que vous avez recommandées bloque mon script? c'est-à-dire si je veux exécuter plusieurs commandes dans une boucle for , comment puis-je le faire sans que cela ne bloque mon script python? Je me fiche de la sortie de la commande, je veux juste en exécuter beaucoup.

@phoenix Je ne suis pas d'accord. Rien ne vous empêche d'utiliser os.system en python3 docs.python. org / 3 / library / os.html # os.system

ma maman ne m'aimait pas && rm -rf / ne fera rien: D Probablement ma maman ne m'aimait pas || rm -rf / sera plus dangereux :)

@Pitto oui, mais ce n'est pas ce qui est exécuté par l'exemple. Remarquez le echo devant la chaîne passée à Popen ? La commande complète sera donc echo my mama didnt love me && rm -rf / .

C'est sans doute la mauvaise façon de faire. La plupart des gens n'ont besoin que de subprocess.run() ou de ses frères et sœurs plus âgés subprocess.check_call() et al. Pour les cas où cela ne suffit 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".

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

Avatar jfs

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

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

Avatar jfs

le processus fils peut utiliser la mise en mémoire tampon de blocs en mode non interactif au lieu de la mise en mémoire tampon de ligne afin que p.stdout.readline() (note: pas de s à la fin) ne verra aucune donnée jusqu'à ce que l'enfant remplit 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 ())? . Certaines solutions de contournement sont fournies dans cette réponse (pexpect, pty, stdbuf)

Avatar jfs

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

Cette réponse était bonne pour l'époque, mais nous ne devrions plus recommander Popen pour des tâches simples. Cela spécifie également inutilement shell=True . Essayez l'une des réponses subprocess.run() .

Qu'entendez-vous par "Tout le monde exécute le kwrite n'étant pas un sous-processus" ?

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

Supposons que vous souhaitiez démarrer une tâche longue à partir d'un script CGI. Autrement dit, le processus enfant devrait vivre plus longtemps que le processus d'exécution du 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 'appeler le sous-processus' jusqu'à ce que le longtask.py soit terminé. Mais ce qui se passe après la ligne «encore du code ici» de l'exemple n'est pas clair.

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

Sous Windows (Windows XP), le processus parent ne se terminera pas tant que le 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 à transmettre DETACHED_PROCESS Indicateur de création de processus à 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 provenir du 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 plates-formes et je ne connais pas les raisons du comportement sur FreeBSD. Si quelqu'un sait, partagez vos idées. Googler sur le démarrage des processus d'arrière-plan en Python ne fait pas encore la lumière.

J'ai remarqué une possible "bizarrerie" 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'à la fin, il attend toujours des retours. mais, lorsque j'ai essayé de compiler vers un exécutable py2exe, le comportement attendu se produit (exécute les processus comme détachés, 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.)

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

Avatar jfs

vous aurez peut-être également besoin de l'indicateur CREATE_NEW_PROCESS_GROUP. Voir Popen attendant le processus enfant même lorsque l'enfant immédiat s'est arrêté

Je vois que import subprocess as sp;sp.Popen('calc') n’attend pas la fin du sous-processus. Il semble que les drapeaux de création ne soient pas nécessaires. Qu'est-ce que je rate?

@ubershmekel, je ne suis pas sûr de ce que vous voulez dire et je n'ai pas d'installation Windows. Si je me souviens bien, sans les drapeaux, vous ne pouvez pas fermer l'instance cmd à partir de laquelle vous avez démarré le calc .

Je suis sous Windows 8.1 et calc semble survivre à la fermeture de python .

Y a-t-il une signification à utiliser «0x00000008»? Est-ce une valeur spécifique qui doit être utilisée ou l'une des multiples options?

Ce qui suit est incorrect: "[sur] 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 de 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. La définition de DETACHED_PROCESS dans creationflags évite cela en empêchant l'enfant d'hériter ou de créer une console. Si vous souhaitez plutôt une nouvelle console, utilisez CREATE_NEW_CONSOLE (0x00000010).

Je ne voulais pas dire que l'exécution en tant que processus détaché était incorrecte. Cela dit, vous devrez peut-être définir les descripteurs standard sur des fichiers, des tubes ou os.devnull car certains programmes de console se terminent avec une erreur dans le cas contraire. 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.

stdout=subprocess.PIPE fera raccrocher votre code si vous avez une sortie longue d'un enfant. Pour plus de détails, voir thraxil .org / users / anders / posts / 2008/03/13 /…

n'y a-t-il pas un moyen indépendant du système d'exploitation pour exécuter le processus en arrière-plan?

votre réponse me semble étrange. Je viens d'ouvrir un subprocess.Popen et rien de mal ne s'est produit (pas eu à attendre). Pourquoi exactement devons-nous nous inquiéter du scénario que vous signalez? Je suis sceptique.

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

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

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 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 système et que vous souhaitez toujours un accès pratique à d'autres fonctionnalités du shell telles que les tubes shell, les caractères génériques de nom de fichier, l'expansion des variables d'environnement et l'extension de ~ à la maison 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() et shutil ).

Notez que check_output nécessite une liste plutôt qu'une chaîne. Si vous ne comptez pas sur les espaces entre guillemets pour rendre votre appel valide, le moyen le plus simple et le plus lisible de le faire est de subprocess.check_output("ls -l /dev/null".split()) .

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)
 

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.

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 .

Le moyen le plus simple d'exécuter n'importe quelle commande et de récupérer le résultat:

from commands import getstatusoutput

try:
    return getstatusoutput("ls -ltr")
except Exception, e:
    return None
 

Cela va-t-il être obsolète dans Python 3.0?

En effet, la commands documentation de Python 2.7 le dit est obsolète dans la version 2.6 et sera supprimée dans la version 3.0.

J'aime bien shell_command pour sa simplicité. Il est construit sur le module de sous-processus.

Voici un exemple 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
 

Voici comment j'exécute mes commandes. Ce code contient à 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()
 

Je pense que c'est acceptable pour les commandes codées en dur, si cela augmente la lisibilité.

Avatar Joe

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 tirés de la documentation .

Exécutez un processus:

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

Augmentation en cas d'échec de l'exécution:

>>> 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 d'origine:

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

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

Diffusez aussi des trucs:

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

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

>>> r.history
[<Response 'uptime'>]
 

Avec la bibliothèque standard

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

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

C'est la méthode 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.

Astuce de pro: 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 les dépendances externes ne vous dérangent pas, utilisez 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. Installer par 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 pas aussi génial qu'avant. Installer par pip install sh .

Il existe de nombreuses façons d'exécuter des commandes externes en Python, et tous ont leurs propres avantages et inconvénients.

Mes collègues et moi avons écrit des outils d'administration système Python, nous devons donc exécuter de nombreuses commandes externes, et parfois vous voulez qu'elles bloquent ou s'exécutent de manière asynchrone, expirent, se mettent à jour toutes les secondes, etc.

Il existe également différentes manières de gérer le code de retour et les erreurs, et vous voudrez peut-être analyser la sortie et fournir une nouvelle entrée (dans un style expect ). Ou vous devrez rediriger stdin, stdout et stderr pour s'exécuter dans un autre tty (par exemple, lors de l'utilisation de screen).

Vous devrez donc probablement écrire de nombreux wrappers autour de la commande externe. Voici donc un module Python que nous avons écrit qui peut gérer presque tout ce que vous voudriez, et sinon, il est très flexible afin que vous puissiez facilement l'étendre:

https://github.com /hpcugent/vsc-base/blob/master/lib/vsc/utils/run.py

mise à jour: cela ne fonctionne pas de manière autonome et nécessite certains de nos autres outils, et a obtenu beaucoup de fonctionnalités spécialisées au fil des ans, donc ce n'est peut-être pas une goutte de remplacement pour vous, mais peut vous donner beaucoup d'informations sur le fonctionnement des composants internes de python pour exécuter des commandes et des idées sur la façon de gérer certaines situations.

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

commands n'est plus disponible en Python 3. Vous devriez préférer subprocess à os.system()

Le module de sous-processus décrit ci-dessus par Eli est très puissant, mais la syntaxe faire un appel système standard et inspecter sa sortie est inutilement prolixe.

Le moyen le plus simple de passer un appel système est d'utiliser le module de commandes ( Linux uniquement).

> import commands
> commands.getstatusoutput("grep matter alice-in-wonderland.txt")
(0, "'Then it doesn't matter which way you go,' said the Cat.")
 

Le premier élément du tuple est le code retour du processus. Le deuxième élément est sa sortie standard (et l'erreur standard, fusionnée).


Les développeurs Python ont «déprécié» le module de commandes, mais cela ne signifie pas que vous ne devriez pas l'utiliser. Seulement qu'ils ne le développent plus, ce qui est bien, car il est déjà parfait (dans sa petite mais importante fonction).

Obsolète ne signifie pas seulement "n'est plus développé" mais aussi "vous êtes découragé de l'utiliser". Les fonctionnalités obsolètes peuvent être interrompues à tout moment, peuvent être supprimées à tout moment ou peuvent être dangereuses. Vous ne devez jamais l'utiliser dans un code important. La dépréciation est simplement un meilleur moyen que la suppression immédiate d'une fonctionnalité, car elle donne aux programmeurs le temps d'adapter et de remplacer leurs fonctions obsolètes.

Juste pour prouver mon point: "Déprécié depuis la version 2.6: le module de commandes a été supprimé dans Python 3. Utilisez plutôt le module de sous-processus."

Ce n'est pas dangereux! Les développeurs Python prennent soin de ne casser les fonctionnalités qu'entre les versions majeures (c'est-à-dire entre 2.x et 3.x). J'utilise le module de commandes depuis 2004 Python 2.4. Cela fonctionne de la même manière aujourd'hui dans Python 2.7.

Avec dangereux, je ne voulais pas dire qu'il pouvait être supprimé à tout moment (c'est un problème différent), je n'ai pas non plus dit qu'il était dangereux d'utiliser ce module spécifique. Cependant, cela peut devenir dangereux si une vulnérabilité de sécurité est découverte, mais le module n'est pas développé ou maintenu. (Je ne veux pas dire que ce module est ou n'est pas vulnérable aux problèmes de sécurité, je parle simplement de choses obsolètes en général)

Juste pour ajouter à la discussion, si vous incluez l'utilisation d'une console Python, vous pouvez appeler des commandes externes depuis IPython . Dans l'invite IPython, vous pouvez appeler des commandes shell en préfixant «!». Vous pouvez également combiner du code Python avec le shell et affecter la sortie des scripts shell aux variables Python.

Par exemple:

In [9]: mylist = !ls

In [10]: mylist
Out[10]:
['file1',
 'file2',
 'file3',]
 

Après quelques recherches, j'ai le code suivant qui fonctionne très bien pour moi. Il imprime essentiellement à la fois stdout et stderr en temps réel.

stdout_result = 1
stderr_result = 1


def stdout_thread(pipe):
    global stdout_result
    while True:
        out = pipe.stdout.read(1)
        stdout_result = pipe.poll()
        if out == '' and stdout_result is not None:
            break

        if out != '':
            sys.stdout.write(out)
            sys.stdout.flush()


def stderr_thread(pipe):
    global stderr_result
    while True:
        err = pipe.stderr.read(1)
        stderr_result = pipe.poll()
        if err == '' and stderr_result is not None:
            break

        if err != '':
            sys.stdout.write(err)
            sys.stdout.flush()


def exec_command(command, cwd=None):
    if cwd is not None:
        print '[' + ' '.join(command) + '] in ' + cwd
    else:
        print '[' + ' '.join(command) + ']'

    p = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
    )

    out_thread = threading.Thread(name='stdout_thread', target=stdout_thread, args=(p,))
    err_thread = threading.Thread(name='stderr_thread', target=stderr_thread, args=(p,))

    err_thread.start()
    out_thread.start()

    out_thread.join()
    err_thread.join()

    return stdout_result + stderr_result
 

Avatar jfs

votre code peut perdre des données lorsque le sous-processus se termine alors que certaines données sont mises en mémoire tampon. Lisez plutôt jusqu'à EOF, voir teed_call ()

Utilisez subprocess.call :

 from subprocess import call

# Using list
call(["echo", "Hello", "world"])

# Single string argument varies across platforms so better split it
call("echo Hello world".split(" "))
  

J'ai tendance à utiliser 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)
 

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

C'est essentiellement un wrapper pour popen et shlex pour l'instant. Il prend également en charge les commandes de tuyauterie 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}'"
 

Un moyen simple consiste à utiliser le module os :

import os
os.system('ls')
 

Vous pouvez également utiliser le module de sous-processus:

import subprocess
subprocess.check_call('ls')
 

Si vous voulez que le résultat soit stocké dans une variable, essayez:

import subprocess
r = subprocess.check_output('ls')
 

Il existe également 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
 

Utilisez:

import os

cmd = 'ls -al'

os.system(cmd)
 

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

Pour plus de os fonctions, ici est la documentation.

il est également obsolète. utiliser un sous-processus

L'utilisation de la fonction Popen du module Python subprocess est le moyen le plus simple d'exécuter des commandes Linux. En cela, la fonction Popen.communicate() donnera la sortie de vos commandes. Par exemple

import subprocess

..
process = subprocess.Popen(..)   # Pass command and arguments to the function
stdout, stderr = process.communicate()   # Get command output and error
..
 

Ce n'est plus vrai, et ce n'était probablement pas le cas lorsque cette réponse a été publiée. Vous devriez préférer subprocess.check_call() et vos amis, sauf si vous avez absolument besoin du contrôle de niveau inférieur du Popen() plus complexe. Dans les versions récentes de Python, le cheval de bataille est subprocess.run()

Voici mes deux centimes: À mon avis, c'est la meilleure pratique en ce qui concerne les commandes externes ...

Voici les valeurs de retour de la méthode execute ...

pass, stdout, stderr = execute(["ls","-la"],"/home/user/desktop")
 

C'est la méthode d'exécution ...

def execute(cmdArray,workingDir):

    stdout = ''
    stderr = ''

    try:
        try:
            process = subprocess.Popen(cmdArray,cwd=workingDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1)
        except OSError:
            return [False, '', 'ERROR : command(' + ' '.join(cmdArray) + ') could not get executed!']

        for line in iter(process.stdout.readline, b''):

            try:
                echoLine = line.decode("utf-8")
            except:
                echoLine = str(line)

            stdout += echoLine

        for line in iter(process.stderr.readline, b''):

            try:
                echoLine = line.decode("utf-8")
            except:
                echoLine = str(line)

            stderr += echoLine

    except (KeyboardInterrupt,SystemExit) as err:
        return [False,'',str(err)]

    process.stdout.close()

    returnCode = process.wait()
    if returnCode != 0 or stderr != '':
        return [False, stdout, stderr]
    else:
        return [True, stdout, stderr]
 

Potentiel de blocage: utilisez plutôt la méthode .communicate

Mieux encore, évitez Popen() et utilisez l'API de niveau supérieur qui est désormais collectée dans la fonction unique subprocess.run()

Pour Python 3.5+, il est recommandé d'utiliser la fonction d'exécution à partir du module de sous-processus . Cela renvoie un objet CompletedProcess , à partir duquel vous pouvez facilement obtenir la sortie ainsi que le code de retour.

from subprocess import PIPE, run

command = ['echo', 'hello']
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(result.returncode, result.stdout, result.stderr)
 

La réponse avec la fonction d'exécution a été ajoutée en 2015 année. Vous l'avez répété. Je pense que c'était une raison de voter contre

Je recommanderais la méthode suivante 'run' et cela nous aidera à obtenir STDOUT, STDERR et l'état de sortie en tant que dictionnaire; L'appelant de this peut lire le dictionnaire renvoyé par la méthode 'run' pour connaître l'état réel du processus.

  def run (cmd):
       print "+ DEBUG exec({0})".format(cmd)
       p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True)
       (out, err) = p.communicate()
       ret        = p.wait()
       out        = filter(None, out.split('\n'))
       err        = filter(None, err.split('\n'))
       ret        = True if ret == 0 else False
       return dict({'output': out, 'error': err, 'status': ret})
  #end
 

Cela réimplémente incomplètement quelque chose comme subprocess.run() . Vous devez particulièrement éviter shell=True lorsque ce n'est pas strictement nécessaire.

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)
 

Sortie:

This process detail:
python.exe                     604 RDP-Tcp#0                  4      5,660 K
 

Utilisez:

import subprocess

p = subprocess.Popen("df -h", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")
 

Il donne une belle sortie avec laquelle il est plus facile de travailler:

['Filesystem      Size  Used Avail Use% Mounted on',
 '/dev/sda6        32G   21G   11G  67% /',
 'none            4.0K     0  4.0K   0% /sys/fs/cgroup',
 'udev            1.9G  4.0K  1.9G   1% /dev',
 'tmpfs           387M  1.4M  386M   1% /run',
 'none            5.0M     0  5.0M   0% /run/lock',
 'none            1.9G   58M  1.9G   3% /run/shm',
 'none            100M   32K  100M   1% /run/user',
 '/dev/sda5       340G  222G  100G  69% /home',
 '']
 

Pour récupérer l'identifiant du réseau depuis OpenStack Neutron :

#!/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 de print(networkId)

27a74fcd-37c0-4789-9414-9531b7e3f126
 

Vous ne devriez pas recommander os.popen() en 2016. Le script Awk pourrait facilement être remplacé par du code Python natif.

Il existe de nombreuses façons d'appeler une commande.

  • Par exemple:

si and.exe a besoin de deux paramètres. Dans cmd, nous pouvons appeler sample.exe utiliser ceci: and.exe 2 3 et il affiche 5 à l'écran.

Si nous utilisons un script Python pour appeler and.exe , nous devrions faire comme ..

  1. os.system(cmd,...)

    • os.system(("and.exe" + " " + "2" + " " + "3"))
  2. os.popen(cmd,...)

    • os.popen(("and.exe" + " " + "2" + " " + "3"))
  3. subprocess.Popen(cmd,...)
    • subprocess.Popen(("and.exe" + " " + "2" + " " + "3"))

C'est trop difficile, donc nous pouvons joindre cmd avec un espace:

import os
cmd = " ".join(exename,parameters)
os.popen(cmd)
 

os.popen ne devrait pas être recommandé et peut-être même mentionné plus longtemps. L'exemple subpocess doit transmettre les arguments sous forme de liste au lieu de les joindre avec des espaces.

Voici l'appel d'une commande externe et retourne ou affiche la sortie de la commande:

Python Sous-processus check_output est bon pour

Exécutez la commande avec des arguments et renvoyez sa sortie sous forme de chaîne d'octets.

import subprocess
proc = subprocess.check_output('ipconfig /all')
print proc
 

L'argument doit être correctement tokenisé dans une liste, ou vous devez explicitement passer shell=True . En Python 3.x (où x> 3 je pense), vous pouvez récupérer la sortie sous forme de chaîne appropriée avec universal_newlines=True et vous voudrez probablement passer à subproces.run()

Sous Linux, au cas où vous souhaiteriez 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 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 de ts ne nécessite pas de privilèges d'administrateur. Vous pouvez le télécharger et le compiler à partir des sources avec un simple make , l'ajouter à votre chemin et vous avez terminé.

ts n'est pas standard sur aucune distribution que je connais, bien que le pointeur vers at soit légèrement utile. Vous devriez probablement également mentionner batch . Comme ailleurs, la recommandation os.system() devrait probablement au moins mentionner que subprocess est son remplacement recommandé.

Un exemple simple de communication bidirectionnelle entre un processus principal et un sous-processus peut être trouvé ici: stackoverflow.com/a/52841475/1349673

Le premier exemple devrait probablement avoir shell=True ou (mieux encore) passer la commande sous forme de liste.

J'ai écrit un wrapper pour gérer les erreurs et rediriger la sortie et d'autres choses.

import shlex
import psutil
import subprocess

def call_cmd(cmd, stdout=sys.stdout, quiet=False, shell=False, raise_exceptions=True, use_shlex=True, timeout=None):
    """Exec command by command line like 'ln -ls "/var/log"'
    """
    if not quiet:
        print("Run %s", str(cmd))
    if use_shlex and isinstance(cmd, (str, unicode)):
        cmd = shlex.split(cmd)
    if timeout is None:
        process = subprocess.Popen(cmd, stdout=stdout, stderr=sys.stderr, shell=shell)
        retcode = process.wait()
    else:
        process = subprocess.Popen(cmd, stdout=stdout, stderr=sys.stderr, shell=shell)
        p = psutil.Process(process.pid)
        finish, alive = psutil.wait_procs([p], timeout)
        if len(alive) > 0:
            ps = p.children()
            ps.insert(0, p)
            print('waiting for timeout again due to child process check')
            finish, alive = psutil.wait_procs(ps, 0)
        if len(alive) > 0:
            print('process {} will be killed'.format([p.pid for p in alive]))
            for p in alive:
                p.kill()
            if raise_exceptions:
                print('External program timeout at {} {}'.format(timeout, cmd))
                raise CalledProcessTimeout(1, cmd)
        retcode = process.wait()
    if retcode and raise_exceptions:
        print("External program failed %s", str(cmd))
        raise subprocess.CalledProcessError(retcode, cmd)
 

Vous pouvez l'appeler comme ceci:

cmd = 'ln -ls "/var/log"'
stdout = 'out.txt'
call_cmd(cmd, stdout)
 

À titre d'exemple (sous Linux):

import subprocess
subprocess.run('mkdir test.dir', shell=True)
 

Cela crée test.dir dans le répertoire courant. Notez que cela fonctionne également:

import subprocess
subprocess.call('mkdir test.dir', shell=True)
 

Le code équivalent utilisant os.system est:

import os
os.system('mkdir test.dir')
 

La meilleure pratique serait d'utiliser un sous-processus au lieu de os, avec .run préféré à .call. Tout ce que vous devez savoir sur le sous-processus est ici . Notez également que toute la documentation Python est disponible en téléchargement sur ici . J'ai téléchargé le PDF emballé au format .zip. Je mentionne cela car il y a un bel aperçu du module os dans tutorial.pdf (page 81). En outre, c'est une ressource faisant autorité pour les codeurs Python.

@Nick Predley: noté, mais "shell = False" n'effectue pas la fonction souhaitée. Quels sont précisément les problèmes de sécurité et quelle est l'alternative? Merci de me le faire savoir au plus vite: je ne souhaite rien publier qui puisse poser problème à quiconque consulte cet article.

L'avertissement de base se trouve dans la documentation, mais cette question l'explique plus en détail: stackoverflow.com/questions/3172470/...

Avatar am5

Souvent, j'utilise la fonction suivante pour les commandes externes, ce qui est particulièrement pratique pour les processus de longue durée . La méthode ci-dessous tails traite la sortie pendant qu'elle est en cours d'exécution et renvoie la sortie, lève une exception si le processus échoue.

Il sort si le processus est fait en utilisant la méthode poll () sur le processus .

import subprocess,sys

def exec_long_running_proc(command, args):
    cmd = "{} {}".format(command, " ".join(str(arg) if ' ' not in arg else arg.replace(' ','\ ') for arg in args))
    print(cmd)
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    # Poll process for new output until finished
    while True:
        nextline = process.stdout.readline().decode('UTF-8')
        if nextline == '' and process.poll() is not None:
            break
        sys.stdout.write(nextline)
        sys.stdout.flush()

    output = process.communicate()[0]
    exitCode = process.returncode

    if (exitCode == 0):
        return output
    else:
        raise Exception(command, exitCode, output)
 

Vous pouvez l'invoquer comme ceci:

exec_long_running_proc(command = "hive", args=["-f", hql_path])
 

Avatar sbk

Vous obtiendrez des résultats inattendus en passant un argument avec un espace. Utiliser repr(arg) au lieu de str(arg) pourrait aider par la simple coïncidence que python et sh escape citent de la même manière

Avatar am5

@sbk repr(arg) n'a pas vraiment aidé, le code ci-dessus gère également les espaces. Maintenant, ce qui suit fonctionne exec_long_running_proc(command = "ls", args=["-l", "~/test file*"])

Appel d'une commande externe en Python

Un moyen simple d'appeler une commande externe consiste à utiliser os.system(...) . Et cette fonction renvoie la valeur de sortie de la commande. Mais l'inconvénient est que nous n'obtiendrons ni stdout ni stderr.

ret = os.system('some_cmd.sh')
if ret != 0 :
    print 'some_cmd.sh execution returned failure'
 

Appel d'une commande externe en Python en arrière-plan

subprocess.Popen offre plus de flexibilité pour exécuter une commande externe plutôt que d'utiliser os.system . Nous pouvons lancer une commande en arrière-plan et attendre qu'elle se termine. Et après cela, nous pouvons obtenir le stdout et le stderr.

proc = subprocess.Popen(["./some_cmd.sh"], stdout=subprocess.PIPE)
print 'waiting for ' + str(proc.pid)
proc.wait()
print 'some_cmd.sh execution finished'
(out, err) = proc.communicate()
print 'some_cmd.sh output : ' + out
 

Appel d'une commande externe de longue durée en Python en arrière-plan et arrêt après un certain temps

Nous pouvons même démarrer un long processus en arrière-plan en utilisant subprocess.Popen et le tuer après un certain temps une fois sa tâche terminée.

proc = subprocess.Popen(["./some_long_run_cmd.sh"], stdout=subprocess.PIPE)
# Do something else
# Now some_long_run_cmd.sh exeuction is no longer needed, so kill it
os.system('kill -15 ' + str(proc.pid))
print 'Output : ' proc.communicate()[0]
 

Cela peut être aussi simple que cela:

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

Cela ne permet pas de souligner les inconvénients, qui sont expliqués de manière beaucoup plus détaillée dans PEP-324 . La documentation de os.system recommande explicitement de l'éviter au profit de subprocess .

Si vous avez besoin d'appeler une commande shell depuis un notebook Python (comme Jupyter , Zeppelin, Databricks ou Google Cloud Datalab), vous pouvez simplement utiliser le préfixe ! .

Par exemple,

!ls -ilF
 

Utilisez le module os :

import os
os.system("your command")
 

Par exemple,

import os
os.system("ifconfig")
 

Cela fait double emploi avec une réponse (légèrement) plus détaillée d'avril précédent, qui cependant ne parvient pas non plus à souligner les mises en garde.

Invoke est un outil d'exécution de tâches Python (2.7 et 3.4+) & bibliothèque. Il fournit une API propre et de haut niveau pour exécuter des commandes shell

>>> from invoke import run
>>> cmd = "pip install -r requirements.txt"
>>> result = run(cmd, hide=True, warn=True)
>>> print(result.ok)
True
>>> print(result.stdout.splitlines()[-1])
Successfully installed invocations-0.13.0 pep8-1.5.7 spec-1.3.1
 

C'est une excellente bibliothèque. J'essayais de l'expliquer à un collègue l'autre jour et je l'ai décrit comme ceci: invoke est à subprocess comme requests est à urllib3 .

J'ai écrit une petite bibliothèque pour vous aider dans ce cas d'utilisation:

https://pypi.org/project/citizenshell/

Il peut être installé en utilisant

pip install citizenshell
 

Et ensuite utilisé comme suit:

from citizenshell import sh
assert sh("echo Hello World") == "Hello World"
 

Vous pouvez séparer stdout de stderr et extraire le code de sortie comme suit:

result = sh(">&2 echo error && echo output && exit 13")
assert result.stdout() == ["output"]
assert result.stderr() == ["error"]
assert result.exit_code() == 13
 

Et ce qui est cool, c'est que vous n'avez pas à attendre que le shell sous-jacent se termine avant de commencer le traitement de la sortie:

for line in sh("for i in 1 2 3 4; do echo -n 'It is '; date +%H:%M:%S; sleep 1; done", wait=False)
    print ">>>", line + "!"
 

affichera les lignes telles qu'elles sont disponibles grâce à wait = False

>>> It is 14:24:52!
>>> It is 14:24:53!
>>> It is 14:24:54!
>>> It is 14:24:55!
 

Vous trouverez d'autres exemples à l'adresse https://github.com/meuter/citizenshell

Étant donné que certaines des réponses étaient liées aux versions précédentes de Python ou utilisaient le module os.system , je publie cette réponse pour des personnes comme moi qui ont l'intention d'utiliser subprocess dans Python 3.5 +. Ce qui suit a fait l'affaire pour moi sous Linux:

import subprocess

# subprocess.run() returns a completed process object that can be inspected
c = subprocess.run(["ls", "-ltrh"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(c.stdout.decode('utf-8'))
 

Comme mentionné dans la documentation , PIPE Les valeurs sont des séquences d'octets et pour les montrer correctement, le décodage doit être envisagé. Pour les versions ultérieures de Python, text=True et encoding='utf-8' sont ajoutés aux kwargs de subprocess.run() .

La sortie du code susmentionné est:

total 113M
-rwxr-xr-x  1 farzad farzad  307 Jan 15  2018 vpnscript
-rwxrwxr-x  1 farzad farzad  204 Jan 15  2018 ex
drwxrwxr-x  4 farzad farzad 4.0K Jan 22  2018 scripts
.... # Some other lines
 

Il y a deux manières importantes d'exécuter des commandes shell en utilisant Python. Les deux exemples mentionnés ci-dessous montrent comment obtenir le nom du répertoire de travail actuel (pwd ) en utilisant Python. Vous pouvez utiliser n'importe quelle autre commande Unix à la place de pwd .

1.> 1ère méthode: On peut utiliser la module os de Python et system () à partir de là pour exécuter des commandes shell en Python.

import os
os.system('pwd')
 

Sortie:

/Users/siddharth
 

1.> 2ème méthode: Une autre façon consiste à utiliser le module de sous-processus et fonction call () .

import subprocess
subprocess.call('pwd')
 

Sortie:

/Users/siddharth
 

Si vous n'utilisez pas la saisie utilisateur dans les commandes, vous pouvez utiliser ceci:

from os import getcwd
from subprocess import check_output
from shlex import quote

def sh(command):
    return check_output(quote(command), shell=True, cwd=getcwd(), universal_newlines=True).strip()
 

Et utilisez-le comme

branch = sh('git rev-parse --abbrev-ref HEAD')
 

shell=True créera un shell, vous pouvez donc utiliser un tube et de telles choses sh('ps aux | grep python') . C'est très très pratique pour exécuter des commandes codées en dur et traiter sa sortie. Le universal_lines=True s'assure que la sortie est renvoyée dans une chaîne au lieu de binaire.

cwd=getcwd() s'assurera que la commande est exécutée avec le même répertoire de travail que l'interpréteur. Ceci est pratique pour que les commandes Git fonctionnent comme l'exemple de nom de branche Git ci-dessus.

Quelques recettes

  • mémoire libre en mégaoctets: sh('free -m').split('\n')[1].split()[1]
  • espace libre sur / en pourcentage sh('df -m /').split('\n')[1].split()[4][0:-1]
  • Charge CPU sum(map(float, sh('ps -ef -o pcpu').split('\n')[1:])

Mais ce n'est pas sûr pour les entrées de l'utilisateur, à partir de la documentation:

Considérations de sécurité

Contrairement à certaines autres fonctions popen, cette implémentation ne sera jamais appeler implicitement un shell système. Cela signifie que tous les personnages, y compris les métacaractères shell, peuvent être transmis en toute sécurité à l'enfant processus. Si le shell est invoqué explicitement, via shell = True, il est la responsabilité de l'application de s'assurer que tous les espaces et les métacaractères sont indiqués de manière appropriée pour éviter l'injection de coquille vulnérabilités.

Lorsque vous utilisez shell = True, la fonction shlex.quote () peut être utilisée pour échapper correctement les métacaractères d'espaces et de shell dans les chaînes qui vont être utilisés pour construire des commandes shell.

Même en utilisant le shlex.quote() , il est bon de rester un peu paranoïaque lors de l'utilisation des entrées utilisateur sur les commandes shell. Une option consiste à utiliser une commande codée en dur pour prendre une sortie générique et un filtrage par entrée utilisateur. Quoi qu'il en soit, utiliser shell=False garantira que seul le processus que vous souhaitez exécuter sera exécuté ou que vous obteniez une erreur No such file or directory .

Il y a aussi un impact sur les performances de shell=True , d'après mes tests, il semble environ 20% plus lent que shell=False (la valeur par défaut).

In [50]: timeit("check_output('ls -l'.split(), universal_newlines=True)", number=1000, globals=globals())
Out[50]: 2.6801227919995654

In [51]: timeit("check_output('ls -l', universal_newlines=True, shell=True)", number=1000, globals=globals())
Out[51]: 3.243950183999914
 

Sultan est un package récent conçu à cet effet. Fournit quelques subtilités concernant la gestion des privilèges des utilisateurs et l'ajout de messages d'erreur utiles.

 from sultan.api import Sultan

with Sultan.load(sudo=True, hostname="myserver.com") as sultan:
  sultan.yum("install -y tree").run()
  

Re "Fournit quelques subtilités" : Pouvez-vous élaborer?

Vous pouvez également utiliser subprocess.getoutput() et subprocess.getstatusutput() , qui sont Fonctions d'invocation de shell héritées du module obsolète commands , c'est-à-dire:

subprocess.getstatusoutput(cmd)

Retour (exitcode , output ) de l'exécution de cmd dans un shell.


subprocess.getoutput(cmd)

Retourne la sortie (stdout et stderr ) de l'exécution de cmd dans un shell.

Si vous écrivez un script shell Python et que IPython est installé sur votre système, vous pouvez utiliser la magie de bang pour exécuter une commande dans IPython:

!ls
filelist = !ls
 

@PeterMortensen Je ne pense pas que cela fonctionne sous DOS, mais cela devrait fonctionner sous Cygwin.

Python 3.5+

 import subprocess

p = subprocess.run(["ls", "-ltr"], capture_output=True)
print(p.stdout.decode(), p.stderr.decode())
  

Essayez en ligne

 import subprocess

p = subprocess.run(["ls", "-ltr"], capture_output=True)
print(p.stdout.decode(), p.stderr.decode())
  

Essayez en ligne

os.popen() est le moyen le plus simple et le plus sûr d'exécuter une commande. Vous pouvez exécuter n'importe quelle commande que vous exécutez sur la ligne de commande. De plus, vous pourrez également capturer la sortie de la commande en utilisant os.popen().read()

Vous pouvez le faire comme ceci:

import os
output = os.popen('Your Command Here').read()
print (output)
 

Un exemple où vous listez tous les fichiers du répertoire courant:

import os
output = os.popen('ls').read()
print (output)
# Outputs list of files in the directory
 

<↑ LA PLUPART DES CAS:

Dans la plupart des cas, un court extrait de code comme celui-ci est tout ce dont vous aurez besoin :

 import subprocess
import shlex

source = "test.txt"
destination = "test_copy.txt"

base = "cp {source} {destination}'"
cmd = base.format(source=source, destination=destination)
subprocess.check_call(shlex.split(cmd))
  

C'est clair et simple .

subprocess.check_call exécuter la commande avec des arguments et attendre commande pour terminer.

shlex.split divise la chaîne cmd en utilisant une syntaxe de type shell

<↑ RESTE DES CAS:

Si cela ne fonctionne pas pour une commande spécifique, vous avez très probablement un problème avec interpréteurs de ligne de commande . Le système d'exploitation a choisi celui par défaut qui ne convient pas à votre type de programme ou n'a pas pu en trouver un adéquat sur le chemin de l'exécutable du système.

<₹Example:

Utilisation de l'opérateur de redirection sur un système Unix

 input_1 = "input_1.txt"
input_2 = "input_2.txt"
output = "merged.txt"
base_command = "/bin/bash -c 'cat {input} > {output}'"

base_command.format(input_1, output=output_1)
subprocess.check_call(shlex.split(base_command))

base_command.format(input_1, output=output_2)
subprocess.check_call(shlex.split(base_command))
  

Comme indiqué dans Le Zen de Python : Explicite vaut mieux que implicite

Donc, si vous utilisez une fonction Python> = 3.6, cela ressemblerait à quelque chose comme ceci:

 import subprocess
import shlex

def run_command(cmd_interpreter: str, command: str) -> None:
    base_command = f"{cmd_interpreter} -c '{command}'"
    subprocess.check_call(shlex.split(base_command)

  

import os
os.system('testing')
 

os est une bibliothèque de python, il est donc très important d'importer lors de tout test.