Je veux écrire une fonction qui exécutera une commande shell et retournera sa sortie sous forme de chaîne , peu importe, est-ce un message d'erreur ou de réussite. Je veux juste obtenir le même résultat que j'aurais obtenu avec la ligne de commande.
Quel serait un exemple de code qui ferait une telle chose?
Par exemple:
def run_command(cmd):
# ??????
print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'
python
shell
subprocess
Silver Light
la source
la source
Réponses:
La réponse à cette question dépend de la version de Python que vous utilisez. L'approche la plus simple consiste à utiliser la
subprocess.check_output
fonction:check_output
exécute un seul programme qui ne prend que des arguments en entrée. 1 Il renvoie le résultat exactement tel qu'impriméstdout
. Si vous devez écrire une entrée dansstdin
, passez directement aux sectionsrun
ouPopen
. Si vous souhaitez exécuter des commandes shell complexes, consultez la noteshell=True
à la fin de cette réponse.La
check_output
fonction fonctionne sur presque toutes les versions de Python encore largement utilisées (2.7+). 2 Mais pour les versions plus récentes, ce n'est plus l'approche recommandée.Versions modernes de Python (3.5 ou supérieur):
run
Si vous utilisez Python 3.5 ou supérieur et n'avez pas besoin de compatibilité descendante , la nouvelle
run
fonction est recommandée. Il fournit une API de haut niveau très générale pour lesubprocess
module. Pour capturer la sortie d'un programme, passez l'subprocess.PIPE
indicateur à l'stdout
argument mot - clé. Accédez ensuite à l'stdout
attribut de l'CompletedProcess
objet retourné :La valeur de retour est un
bytes
objet, donc si vous voulez une chaîne correcte, vous en aurez besoindecode
. En supposant que le processus appelé renvoie une chaîne codée en UTF-8:Tout cela peut être compressé en une seule ligne:
Si vous souhaitez transmettre une entrée aux processus
stdin
, passez unbytes
objet à l'input
argument mot - clé:Vous pouvez capturer des erreurs en passant
stderr=subprocess.PIPE
(capture àresult.stderr
) oustderr=subprocess.STDOUT
(capture àresult.stdout
avec sortie régulière). Lorsque la sécurité n'est pas un problème, vous pouvez également exécuter des commandes shell plus complexes en passantshell=True
comme décrit dans les notes ci-dessous.Cela ajoute juste un peu de complexité par rapport à l'ancienne façon de faire les choses. Mais je pense que ça vaut le coup: maintenant vous pouvez faire presque tout ce que vous devez faire avec la
run
fonction seule.Versions plus anciennes de Python (2.7-3.4):
check_output
Si vous utilisez une ancienne version de Python ou si vous avez besoin d'une rétrocompatibilité modeste, vous pouvez probablement utiliser la
check_output
fonction décrite brièvement ci-dessus. Il est disponible depuis Python 2.7.Il prend les mêmes arguments que
Popen
(voir ci-dessous) et renvoie une chaîne contenant la sortie du programme. Le début de cette réponse contient un exemple d'utilisation plus détaillé. En Python 3.5 et supérieur,check_output
équivaut à exécuterrun
aveccheck=True
etstdout=PIPE
et à ne renvoyer que l'stdout
attribut.Vous pouvez passer
stderr=subprocess.STDOUT
pour vous assurer que les messages d'erreur sont inclus dans la sortie renvoyée - mais dans certaines versions de Python, le passagestderr=subprocess.PIPE
àcheck_output
peut provoquer des blocages . Lorsque la sécurité n'est pas un problème, vous pouvez également exécuter des commandes shell plus complexes en passantshell=True
comme décrit dans les notes ci-dessous.Si vous avez besoin de diriger
stderr
ou de transmettre des données au processus, vouscheck_output
ne serez pas à la hauteur de la tâche. Voir lesPopen
exemples ci-dessous dans ce cas.Applications complexes et versions héritées de Python (2.6 et inférieures):
Popen
Si vous avez besoin d'une compatibilité ascendante profonde ou si vous avez besoin de fonctionnalités plus sophistiquées que celles
check_output
fournies, vous devrez travailler directement avec desPopen
objets, qui encapsulent l'API de bas niveau pour les sous-processus.Le
Popen
constructeur accepte soit une seule commande sans arguments, soit une liste contenant une commande comme premier élément, suivie d'un nombre quelconque d'arguments, chacun comme un élément distinct dans la liste.shlex.split
peut aider à analyser les chaînes dans des listes correctement formatées.Popen
Les objets acceptent également une multitude d'arguments différents pour la gestion des E / S de processus et la configuration de bas niveau.Pour envoyer des entrées et capturer des sorties,
communicate
c'est presque toujours la méthode préférée. Un péché:Ou
Si vous définissez
stdin=PIPE
,communicate
vous permet également de transmettre des données au processus viastdin
:Note Aaron réponse de Hall , qui indique que sur certains systèmes, vous devrez peut - être ensemble
stdout
,stderr
etstdin
tousPIPE
(ouDEVNULL
) pour obtenircommunicate
au travail du tout.Dans certains cas rares, vous pouvez avoir besoin d'une capture de sortie complexe en temps réel. La réponse de Vartec suggère une voie à suivre, mais des méthodes autres que celles
communicate
sujettes à des blocages ne sont pas utilisées avec précaution.Comme avec toutes les fonctions ci-dessus, lorsque la sécurité n'est pas un problème, vous pouvez exécuter des commandes shell plus complexes en passant
shell=True
.Remarques
1. Exécuter des commandes shell: l'
shell=True
argumentNormalement, chaque appel à
run
,check_output
ou lePopen
constructeur exécute un seul programme . Cela signifie pas de tuyaux de style bash fantaisie. Si vous souhaitez exécuter des commandes shell complexes, vous pouvez passershell=True
, que les trois fonctions prennent en charge.Cependant, cela pose des problèmes de sécurité . Si vous faites autre chose que des scripts légers, vous feriez mieux d'appeler chaque processus séparément et de passer la sortie de chacun comme entrée au suivant, via
Ou
La tentation de connecter directement les tuyaux est forte; Résiste. Sinon, vous verrez probablement des blocages ou devrez faire des choses comme ça .
2. Considérations Unicode
check_output
renvoie une chaîne en Python 2, mais unbytes
objet en Python 3. Cela vaut la peine de prendre un moment pour en apprendre unicode si vous ne l'avez pas déjà fait.la source
check_output()
etcommunicate()
vous devez attendre que le processus soit terminé,poll()
vous obtenez la sortie au fur et à mesure. Cela dépend vraiment de ce dont vous avez besoin.out
était de type<class 'bytes'>
pour moi. Afin d'obtenir la sortie sous forme de chaîne, j'ai dû la décoder avant d'imprimer commeout.decode("utf-8")
shell=True
? Ça marche pour moi. Vous n'avez pas besoin deshlex.split
passershell=True
.shlex.split
est pour les commandes non shell. Je pense que je vais retirer ce morceau parce que cela embrouille les eaux.universal_newlines=True
qui vous permet de passer et de retirer des chaînes Unicode dans l'encodage par défaut du système. En 3.7, cela a été renommé le plus sensibletext=True
.encoding
paramètre desubprocess.run
au lieu d'utiliserresult.stdout.decode('utf-8')
, vous pouvez utilisersubprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8')
.C'est beaucoup plus facile, mais ne fonctionne que sur Unix (y compris Cygwin) et Python2.7.
Il retourne un tuple avec (return_value, output).
Pour une solution qui fonctionne à la fois en Python2 et Python3, utilisez
subprocess
plutôt le module:la source
Quelque chose comme ca:
Notez que je redirige stderr vers stdout, ce n'est peut-être pas exactement ce que vous voulez, mais je veux aussi des messages d'erreur.
Cette fonction produit ligne par ligne au fur et à mesure (normalement, vous devez attendre la fin du sous-processus pour obtenir la sortie dans son ensemble).
Pour votre cas, l'utilisation serait:
la source
wait
et lescall
fonctions.PIPE
valeurstdin
et de fermer ce fichier dès qu'ilPopen
revient.retcode
est0
. Le chèque devrait êtreif retcode is not None
. Vous ne devriez pas donner des chaînes vides (même une ligne vide est au moins un symbole « \ n »):if line: yield line
. Appelezp.stdout.close()
à la fin..readlines()
ne reviendra pas tant que toutes les sorties n'auront pas été lues et par conséquent il casse pour les sorties de grande taille qui ne tiennent pas en mémoire. De plus, pour éviter de manquer des données tamponnées après la fin du sous-processus, il devrait y avoir un analogue deif retcode is not None: yield from p.stdout.readlines(); break
La réponse de Vartec ne lit pas toutes les lignes, j'ai donc fait une version qui l'a fait:
L'utilisation est la même que la réponse acceptée:
la source
return iter(p.stdout.readline, b'')
place de la boucle whilep.stdout.readline()
peut retourner la sortie non vide précédemment mise en mémoire tampon même si le processus enfant a déjà quitté (p.poll()
n'est pasNone
).Il s'agit d'une solution délicate mais super simple qui fonctionne dans de nombreuses situations:
Un fichier temporaire (ici tmp) est créé avec la sortie de la commande et vous pouvez y lire la sortie souhaitée.
Note supplémentaire dans les commentaires: vous pouvez supprimer le fichier tmp dans le cas d'un travail unique. Si vous devez le faire plusieurs fois, il n'est pas nécessaire de supprimer le tmp.
la source
mktemp
pour le faire fonctionner dans des situations filetées je supposeos.remove('tmp')
pour la rendre "sans fichier".J'ai eu le même problème mais j'ai trouvé un moyen très simple de le faire:
J'espère que ça aide
Remarque: Cette solution est spécifique à Python3 car
subprocess.getoutput()
elle ne fonctionne pas dans Python2la source
Vous pouvez utiliser les commandes suivantes pour exécuter n'importe quelle commande shell. Je les ai utilisés sur Ubuntu.
Remarque: Ceci est obsolète depuis python 2.6. Vous devez maintenant utiliser
subprocess.Popen
. Voici l'exemplela source
os.popen()
est déconseillé dans Python 2.6, mais il n'est pas déconseillé dans Python 3.x, car dans 3.x il est implémenté en utilisantsubprocess.Popen()
.Votre kilométrage peut varier, j'ai tenté de faire tourner @ senderle sur la solution de Vartec dans Windows sur Python 2.6.5, mais je recevais des erreurs et aucune autre solution n'a fonctionné. Mon erreur était:
WindowsError: [Error 6] The handle is invalid
.J'ai trouvé que je devais assigner PIPE à chaque poignée pour qu'elle renvoie la sortie que j'attendais - ce qui suit a fonctionné pour moi.
et appelez comme ceci, (
[0]
obtient le premier élément du tuple,stdout
):Après avoir appris plus, je crois que j'ai besoin de ces arguments de pipe parce que je travaille sur un système personnalisé qui utilise différentes poignées, j'ai donc dû contrôler directement tous les std.
Pour arrêter les fenêtres contextuelles de la console (avec Windows), procédez comme suit:
la source
DEVNULL
au lieu desubprocess.PIPE
si vous n'écrivez / ne lisez pas à partir d'un canal, sinon vous risquez de bloquer le processus enfant.J'avais une saveur légèrement différente du même problème avec les exigences suivantes:
mot-clé 'yield' ci-dessus
J'ai combiné et peaufiné les réponses précédentes pour arriver aux éléments suivants:
Ce code serait exécuté de la même manière que les réponses précédentes:
la source
p.poll()
sans sommeil entre les appels, nous gaspillerions les cycles CPU en appelant cette fonction des millions de fois. Au lieu de cela, nous "étranglons" notre boucle en disant au système d'exploitation que nous n'avons pas besoin d'être dérangés pour le 1 / 10ème de seconde suivant, afin qu'il puisse effectuer d'autres tâches. (Il est possible que p.poll () dorme également, ce qui rend notre déclaration de sommeil redondante).La division de la commande initiale pour le
subprocess
peut être délicate et lourde.Utilisez
shlex.split()
pour vous aider.Exemple de commande
git log -n 5 --since "5 years ago" --until "2 year ago"
Le code
Sans
shlex.split()
le code se présenterait comme suitla source
shlex.split()
est pratique, surtout si vous ne savez pas exactement comment fonctionne la citation dans le shell; mais la conversion manuelle de cette chaîne dans la liste['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']
n'est pas difficile du tout si vous comprenez la citation.Si vous devez exécuter une commande shell sur plusieurs fichiers, cela a fait l'affaire pour moi.
Edit: Je viens de voir la solution de Max Persson avec la suggestion de JF Sebastian. Avancé et incorporé cela.
la source
Popen
accepte soit une chaîne, mais alors vous avez besoinshell=True
, ou une liste d'arguments, auquel cas vous devez passer à la['nm', filename]
place d'une chaîne. Ce dernier est préférable car le shell ajoute de la complexité sans apporter de valeur ici. Passer une chaîne sansshell=True
apparemment fonctionne sur Windows, mais cela pourrait changer dans n'importe quelle prochaine version de Python.Selon @senderle, si vous utilisez python3.6 comme moi:
Agira exactement comme si vous exécutiez la commande dans bash
la source
check=True, universal_newlines=True
. En d'autres termes, faitsubprocess.run()
déjà tout ce que fait votre code.Si vous utilisez le
subprocess
module python, vous pouvez gérer le STDOUT, le STDERR et renvoyer le code de commande séparément. Vous pouvez voir un exemple pour l'implémentation complète de l'appelant de commande. Bien sûr, vous pouvez l'étendretry..except
si vous le souhaitez.La fonction ci-dessous renvoie le code STDOUT, STDERR et Return afin que vous puissiez les gérer dans l'autre script.
la source
subprocess.run()
. Ne réinventez pas la roue.par exemple, exécuter ('ls -ahl') différencié trois / quatre retours possibles et plateformes OS:
fonction ci-dessous
la source
La sortie peut être redirigée vers un fichier texte, puis relue.
la source
stdout=temp_file
?ecls.exe
semble déployer un autre outil de ligne de commande, donc la méthode simple ne fonctionnait pas parfois.vient d'écrire un petit script bash pour ce faire en utilisant curl
https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5
la source