Exécuter la commande shell à partir de Python et capturer la sortie
je veux écrire une fonction qui exécutera une commande shell et retournera sa sortie comme une chaîne de caractères , peu importe, Est-ce un message d'erreur ou de succès. 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'
13 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 fonction subprocess.check_output
:
>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
check_output
exécute un seul programme qui ne prend que les arguments en entrée. 1 il renvoie le résultat exactement tel qu'imprimé à stdout
. Si vous devez écrire stdin
, allez aux sections run
ou Popen
. Si vous pour exécuter des commandes shell complexes, voir la note sur shell=True
à la fin de cette réponse.
la fonction check_output
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 plus): run
si vous utilisez Python 3.5 ou plus haut, et n'ont pas besoin de rétrocompatibilité , la nouvelle run
fonction est recommandé. Il fournit une API de haut niveau très générale pour le module subprocess
. Pour capturer la sortie d'un programme, passez le drapeau subprocess.PIPE
à l'argument de mot-clé stdout
. Puis accédez à l'attribut stdout
de l'objet retourné CompletedProcess
:
>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
Le retour la valeur est un objet bytes
, donc si vous voulez une chaîne appropriée, vous aurez besoin de decode
. En supposant que le processus appelé renvoie une chaîne de caractères encodée UTF-8:
>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
tout cela peut être comprimé en une seule doublure:
>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
si vous voulez passer l'entrée au processus stdin
, passer un bytes
objet au input
argument de mot clé:
>>> cmd = ['awk', 'length("151940920") > 5']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'
vous pouvez capturer des erreurs en passant stderr=subprocess.PIPE
(capture à result.stderr
) ou stderr=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 passant shell=True
comme décrit dans les notes ci-dessous.
Cela ajoute 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 fonction run
seule.
anciennes versions de Python (2.7-3.4): check_output
si vous utilisez une ancienne version de Python, ou si vous avez besoin d'une modeste compatibilité ascendante, vous pouvez probablement utiliser la fonction check_output
comme décrit brièvement ci-dessus. Il est disponible depuis Python 2.7.
subprocess.check_output(*popenargs, **kwargs)
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 est plus exemple d'utilisation détaillé.
vous pouvez passer stderr=subprocess.STDOUT
pour vous assurer que les messages d'erreur sont inclus dans la sortie retournée -- mais ne passez pas stderr=subprocess.PIPE
à check_output
. Il peut causer blocages . Lorsque la sécurité n'est pas un problème, vous pouvez également exécuter des commandes shell plus complexes en passant shell=True
comme décrit dans les notes ci-dessous.
si vous avez besoin de pipe de stderr
ou passer entrée au processus, check_output
ne sera pas à la hauteur. Voir les exemples Popen
ci-dessous dans ce cas.
applications Complexes et anciennes versions de Python (2.6 et ci-dessous): Popen
si vous avez besoin d'une compatibilité en arrière profonde, ou si vous avez besoin de fonctionnalités plus sophistiquées que celles de check_output
, vous devrez travailler directement avec les objets Popen
, qui encapsulent l'API de bas niveau pour les sous-processus.
le Popen
constructeur accepte une seule commande sans arguments, ou une liste contenant une commande par son premier élément, suivi d'un nombre quelconque d'arguments, chacun d'eux comme un élément distinct dans la liste. shlex.split
peut aider les chaînes de caractères dans des listes correctement formatées. Les objets Popen
acceptent aussi un host of different arguments pour la gestion de processus et de bas niveau configuration.
pour envoyer des entrées et des sorties de capture, communicate
est presque toujours la méthode préférée. Comme dans:
output = subprocess.Popen(["mycmd", "myarg"],
stdout=subprocess.PIPE).communicate()[0]
ou
>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE,
... stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo
si vous définissez stdin=PIPE
, communicate
vous permet également de passer des données au processus via stdin
:
>>> cmd = ['awk', 'length("151980920") > 5']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
... stderr=subprocess.PIPE,
... stdin=subprocess.PIPE)
>>> out, err = p.communicate('foo\nfoofoo\n')
>>> print out
foofoo
Note réponse D'Aaron Hall , qui indique que sur certains systèmes, vous devrez peut-être définir stdout
, stderr
, et stdin
tout à PIPE
(ou DEVNULL
) pour obtenir communicate
de travailler à tous.
dans certains cas rares, vous pouvez avoir besoin complexe, la capture de sortie en temps réel. Vartec ' S réponse suggère une voie à suivre, mais les méthodes autres que communicate
sont sujettes à des blocages si elles ne sont pas utilisées avec soin.
Comme avec toutes les fonctions ci-dessus, lorsque la sécurité n'est pas un problème, vous pouvez exécuter plus commandes shell complexes en passant shell=True
.
Notes
1. Exécution des commandes shell: l'argument shell=True
Normalement, chaque appel à run
, check_output
, ou le Popen
constructeur exécute un programme unique . Ça veut dire pas de pipes de style bash. Si vous voulez exécuter des commandes shell complexes, vous pouvez passer shell=True
, que les trois les fonctions de soutien.
Cependant, cela soulève 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 une entrée à la suivante, via
run(cmd, [stdout=etc...], input=other_output)
ou
Popen(cmd, [stdout=etc...]).communicate(other_output)
la tentation de raccorder directement les tuyaux est forte; résistez-y. Autrement, vous verrez probablement des blocages ou à faire des choses comme ce .
2. Unicode considérations
check_output
renvoie une chaîne en Python 2, mais un objet bytes
en Python 3. Cela vaut la peine de prendre un moment pour en savoir plus sur unicode si vous ne l'avez pas déjà.
c'est beaucoup plus facile, mais ne fonctionne que sur Unix (y compris Cygwin).
import commands
print commands.getstatusoutput('wc -l file')
il retourne un tuple avec le (return_value, output)
quelque chose comme ça:
def runProcess(exe):
p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while(True):
retcode = p.poll() #returns None while subprocess is running
line = p.stdout.readline()
yield line
if(retcode is not None):
break
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 comme ils viennent (normalement, vous auriez à attendre la fin de subprocess pour obtenir la sortie dans son ensemble).
pour votre cas l'usage serait:
for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
print line,
la réponse de Vartec ne lit pas toutes les lignes, donc j'ai fait une version qui a fait:
def run_command(command):
p = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
return iter(p.stdout.readline, b'')
L'Usage est le même que la réponse acceptée:
command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
print(line)
il s'agit d'une délicate mais super simple solution qui fonctionne dans de nombreuses situations:
import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()
un fichier temporaire(ici tmp) est créé avec la sortie de la commande et vous pouvez en lire la sortie désirée.
note supplémentaire des commentaires: Vous pouvez supprimer le fichier tmp en cas de travail ponctuel. Si vous devez le faire plusieurs fois, il n'est pas nécessaire de supprimer le tmp.
os.remove('tmp')
Modern Python solution (>=3.1):
res = subprocess.check_output(lcmd, stderr=subprocess.STDOUT)
En Python 3.5:
import subprocess
output = subprocess.run("ls -l", shell=True, stdout=subprocess.PIPE,
universal_newlines=True)
print(output.stdout)
vous pouvez utiliser les commandes suivantes pour exécuter n'importe quelle commande shell. Je les ai utilisés sur ubuntu.
import os
os.popen('your command here').read()
votre kilométrage peut varier, j'ai essayé le spin de @senderle sur la solution de Vartec dans Windows sur Python 2.6.5, mais j'ai eu 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 des tuyaux à chaque poignée pour obtenir le retour de la production que je m'attendais - les suivants ont fonctionné pour moi.
import subprocess
def run_command(cmd):
"""given shell command, returns communication tuple of stdout and stderr"""
return subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE).communicate()
et appelez comme ceci, ( [0]
obtient le premier élément du tuple, stdout
):
run_command('tracert 11.1.0.1')[0]
après avoir appris plus, je crois que j'ai besoin de ces arguments pipe parce que je travaille sur un système personnalisé qui utilise des poignées différentes, donc j'ai dû contrôler directement toutes les MST.
pour arrêter les popups de console( avec Windows), faites ceci:
def run_command(cmd):
"""given shell command, returns communication tuple of stdout and stderr"""
# instantiate a startupinfo obj:
startupinfo = subprocess.STARTUPINFO()
# set the use show window flag, might make conditional on being in Windows:
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
# pass as the startupinfo keyword argument:
return subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
startupinfo=startupinfo).communicate()
run_command('tracert 11.1.0.1')
j'ai eu le même problème Mais trouvé un moyen très simple de le faire suivre cette
import subprocess
Input = subprocess.getoutput("ls -l")
print(Input)
j'Espère que ça aide
Note: Cette solution est spécifique à python3 en tant que subprocess.getoutput()
ne fonctionne pas en python2
j'avais un goût légèrement différent du même problème avec les exigences suivantes:
- Capture et renvoie les messages STDOUT au fur et à mesure qu'ils s'accumulent dans le tampon STDOUT (c'est-à-dire en temps réel).
- @vartec a résolu cela Pythoniquement avec son utilisation de générateurs et le "rendement "
mot-clé au-dessus de
- @vartec a résolu cela Pythoniquement avec son utilisation de générateurs et le "rendement "
- Print toutes les lignes STDOUT ( même si le processus se termine avant le tampon STDOUT peuvent être lues )
- Ne perdez pas de cycles CPU de l'interrogation du processus à haute fréquence", 1519140920"
- Vérifier le code de retour de la sous-procédure
- Imprimer STDERR (séparé de STDOUT) si nous obtenons un code de retour d'erreur non-zéro.
:
import subprocess
from time import sleep
def run_command(command):
p = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True)
# Read stdout from subprocess until the buffer is empty !
for line in iter(p.stdout.readline, b''):
if line: # Don't print blank lines
yield line
# This ensures the process has completed, AND sets the 'returncode' attr
while p.poll() is None:
sleep(.1) #Don't waste CPU-cycles
# Empty STDERR buffer
err = p.stderr.read()
if p.returncode != 0:
# The run_command() function is responsible for logging STDERR
print("Error: " + str(err))
ce code serait exécuté comme les réponses précédentes:
for line in run_command(cmd):
print(line)
Si vous avez besoin d'exécuter une commande shell sur plusieurs fichiers, cela a fait l'affaire pour moi.
import os
import subprocess
# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec's solution because it wasn't printing all lines)
def runProcess(exe):
p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return iter(p.stdout.readline, b'')
# Get all filenames in working directory
for filename in os.listdir('./'):
# This command will be run on each file
cmd = 'nm ' + filename
# Run the command and capture the output line by line.
for line in runProcess(cmd.split()):
# Eliminate leading and trailing whitespace
line.strip()
# Split the output
output = line.split()
# Filter the output and print relevant lines
if len(output) > 2:
if ((output[2] == 'set_program_name')):
print filename
print line
Edit: Viens de voir Max Persson solution avec J. F. Sebastian de la suggestion. Allé de l'avant et intégrées.
par exemple, execute ("ls -lah') différencié trois/quatre retours et les plateformes OS:
- pas de sortie, mais de l'exécuter avec succès
- sortie ligne vide, exécution réussie
- exécuter en "échec de l'151950920"
- la sortie de quelque chose, d'exécuter avec succès
fonction ci-dessous
def execute(cmd, output=True, DEBUG_MODE=False):
"""Executes a bash command.
(cmd, output=True)
output: whether print shell output to screen, only affects screen display, does not affect returned values
return: ...regardless of output=True/False...
returns shell output as a list with each elment is a line of string (whitespace stripped both sides) from output
could be
[], ie, len()=0 --> no output;
[''] --> output empty line;
None --> error occured, see below
if error ocurs, returns None (ie, is None), print out the error message to screen
"""
if not DEBUG_MODE:
print "Command: " + cmd
# https://stackoverflow.com/a/40139101/2292993
def _execute_cmd(cmd):
if os.name == 'nt' or platform.system() == 'Windows':
# set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
else:
# Use bash; the default is sh
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable="/bin/bash")
# the Popen() instance starts running once instantiated (??)
# additionally, communicate(), or poll() and wait process to terminate
# communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as tuple
# if communicate(), the results are buffered in memory
# Read stdout from subprocess until the buffer is empty !
# if error occurs, the stdout is '', which means the below loop is essentially skipped
# A prefix of 'b' or 'B' is ignored in Python 2;
# it indicates that the literal should become a bytes literal in Python 3
# (e.g. when code is automatically converted with 2to3).
# return iter(p.stdout.readline, b'')
for line in iter(p.stdout.readline, b''):
# # Windows has \r\n, Unix has \n, Old mac has \r
# if line not in ['','\n','\r','\r\n']: # Don't print blank lines
yield line
while p.poll() is None:
sleep(.1) #Don't waste CPU-cycles
# Empty STDERR buffer
err = p.stderr.read()
if p.returncode != 0:
# responsible for logging STDERR
print("Error: " + str(err))
yield None
out = []
for line in _execute_cmd(cmd):
# error did not occur earlier
if line is not None:
# trailing comma to avoid a newline (by print itself) being printed
if output: print line,
out.append(line.strip())
else:
# error occured earlier
out = None
return out
else:
print "Simulation! The command is " + cmd
print ""