Stocker la sortie de l'appel subprocess.Popen dans une chaîne


300

J'essaie de faire un appel système en Python et de stocker la sortie dans une chaîne que je peux manipuler dans le programme Python.

#!/usr/bin/python
import subprocess
p2 = subprocess.Popen("ntpq -p")

J'ai essayé quelques choses, y compris certaines des suggestions ici:

Récupération de la sortie de subprocess.call ()

mais sans aucune chance.


3
Il est toujours bon de publier le code réel que vous avez exécuté et le comportement de trace ou le comportement inattendu réel pour des questions concrètes comme celle-ci. Par exemple, je ne sais pas ce que vous avez essayé de faire pour obtenir la sortie et je soupçonne que vous n’avez pas été si loin pour commencer - vous auriez eu une erreur de ne pas trouver le fichier "ntpq -p", ce qui est une partie différente de le problème que vous demandez.
Mike Graham

Réponses:


467

En Python 2.7 ou Python 3

Au lieu de créer un Popenobjet directement, vous pouvez utiliser la subprocess.check_output()fonction pour stocker la sortie d'une commande dans une chaîne:

from subprocess import check_output
out = check_output(["ntpq", "-p"])

En Python 2.4-2.6

Utilisez la communicateméthode.

import subprocess
p = subprocess.Popen(["ntpq", "-p"], stdout=subprocess.PIPE)
out, err = p.communicate()

out c'est ce que tu veux.

Remarque importante sur les autres réponses

Notez comment j'ai passé la commande. L' "ntpq -p"exemple soulève une autre question. Puisque Popenn'invoque pas le shell, vous utiliseriez une liste de commandes et d'options— ["ntpq", "-p"].


3
Dans ce cas, python attend-il que cet appel système se termine? Ou est-il nécessaire d'appeler explicitement la fonction wait / waitpid?
NoneType

7
@NoneType, Popen.communicatene revient pas tant que le processus n'est pas terminé.
Mike Graham

10
si vous voulez obtenir un flux d'erreur, ajoutez stderr:p = subprocess.Popen(["ntpq", "-p"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Timofey

82
Attention, subprocess.check_output()retourne un bytesobjet, pas un str. Si tout ce que vous voulez faire, c'est imprimer le résultat, cela ne fera aucune différence. Mais si vous souhaitez utiliser une méthode telle que myString.split("\n")sur le résultat, vous devrez d'abord décoder l' bytesobjet: subprocess.check_output(myParams).decode("utf-8")par exemple.
TanguyP

17
l'ajout en universal_newlines=Truetant que paramètre m'a aidé dans Python 3 à obtenir l'objet chaîne. Si universal_newlines a la valeur True, ils sont ouverts en mode texte avec l'encodage par défaut. Sinon, ils sont ouverts en tant que flux binaires.
Jonathan Komar

38

Cela a fonctionné pour moi pour rediriger stdout (stderr peut être géré de la même manière):

from subprocess import Popen, PIPE
pipe = Popen(path, stdout=PIPE)
text = pipe.communicate()[0]

Si cela ne fonctionne pas pour vous, veuillez spécifier exactement le problème que vous rencontrez.


3
Cela produit un objet étrange. Quand je le convertis en chaîne, il échappe aux espaces comme \n.
Tomáš Zato - Rétablir Monica

1
Notez que cela ne vérifie pas si le sous-processus s'est correctement exécuté. Vous voudrez probablement vérifier cela également pipe.returncode == 0après la dernière ligne.
thakis

la raison pour laquelle cela fonctionne est parce que Popenrenvoie un tuple de stdoutet stderr, donc lorsque vous accédez, [0]vous saisissez simplement le stdout. Vous pourriez aussi faire text, err = pipe.communicate()et vous textaurez alors ce que vous attendez
Jona

23

En supposant que ce pwdn'est qu'un exemple, voici comment vous pouvez le faire:

import subprocess

p = subprocess.Popen("pwd", stdout=subprocess.PIPE)
result = p.communicate()[0]
print result

Voir la documentation du sous - processus pour un autre exemple et plus d'informations.


22

subprocess.Popen: http://docs.python.org/2/library/subprocess.html#subprocess.Popen

import subprocess

command = "ntpq -p"  # the shell command
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=None, shell=True)

#Launch the shell command:
output = process.communicate()

print output[0]

Dans le constructeur Popen, si shell vaut True , vous devez passer la commande sous forme de chaîne plutôt que sous forme de séquence. Sinon, divisez simplement la commande en une liste:

command = ["ntpq", "-p"]  # the shell command
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=None)

Si vous devez également lire l'erreur standard, dans l'initialisation Popen, vous pouvez définir stderr sur subprocess.PIPE ou sur subprocess.STDOUT :

import subprocess

command = "ntpq -p"  # the shell command
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

#Launch the shell command:
output, error = process.communicate()

génial c'est ce que je cherchais
kRazzy R

14

pour Python 2.7+, la réponse idiomatique est d'utiliser subprocess.check_output()

Vous devriez également noter la gestion des arguments lors de l'appel d'un sous-processus, car cela peut être un peu déroutant ....

Si args est une commande unique sans argument propre (ou si vous l'avez shell=Truedéfini), il peut s'agir d'une chaîne. Sinon, ce doit être une liste.

par exemple ... pour appeler la lscommande, c'est bien:

from subprocess import check_call
check_call('ls')

il en est de même:

from subprocess import check_call
check_call(['ls',])

cependant, si vous voulez passer quelques arguments à la commande shell, vous ne pouvez pas faire ceci:

from subprocess import check_call
check_call('ls -al')

à la place, vous devez le transmettre sous forme de liste:

from subprocess import check_call
check_call(['ls', '-al'])

la shlex.split()fonction peut parfois être utile pour diviser une chaîne en syntaxe de type shell avant de créer un sous-processus ... comme ceci:

from subprocess import check_call
import shlex
check_call(shlex.split('ls -al'))

Cinq ans plus tard, cette question suscite toujours beaucoup d'amour. Merci pour la mise à jour 2.7+, Corey!
Mark

11

Cela fonctionne parfaitement pour moi:

import subprocess
try:
    #prints results and merges stdout and std
    result = subprocess.check_output("echo %USERNAME%", stderr=subprocess.STDOUT, shell=True)
    print result
    #causes error and merges stdout and stderr
    result = subprocess.check_output("copy testfds", stderr=subprocess.STDOUT, shell=True)
except subprocess.CalledProcessError, ex: # error code <> 0 
    print "--------error------"
    print ex.cmd
    print ex.message
    print ex.returncode
    print ex.output # contains stdout and stderr together 

9

C'était parfait pour moi. Vous obtiendrez le code retour, stdout et stderr dans un tuple.

from subprocess import Popen, PIPE

def console(cmd):
    p = Popen(cmd, shell=True, stdout=PIPE)
    out, err = p.communicate()
    return (p.returncode, out, err)

Par exemple:

result = console('ls -l')
print 'returncode: %s' % result[0]
print 'output: %s' % result[1]
print 'error: %s' % result[2]

6

La réponse acceptée est toujours bonne, juste quelques remarques sur les nouvelles fonctionnalités. Depuis python 3.6, vous pouvez gérer l'encodage directement dans check_output, voir la documentation . Cela renvoie un objet chaîne maintenant:

import subprocess 
out = subprocess.check_output(["ls", "-l"], encoding="utf-8")

En python 3.7, un paramètre a capture_outputété ajouté à subprocess.run (), qui effectue une partie de la gestion de Popen / PIPE pour nous, voir les documents python :

import subprocess 
p2 = subprocess.run(["ls", "-l"], capture_output=True, encoding="utf-8")
p2.stdout

4
 import os   
 list = os.popen('pwd').read()

Dans ce cas, vous n'aurez qu'un seul élément dans la liste.


7
os.popenest déconseillé au profit du subprocessmodule.
Mike Graham

3
Cela a été très utile pour l'administrateur sur une ancienne boîte utilisant la série 2.2.X de Python.
Neil McF

4

J'ai écrit une petite fonction basée sur les autres réponses ici:

def pexec(*args):
    return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0].rstrip()

Usage:

changeset = pexec('hg','id','--id')
branch = pexec('hg','id','--branch')
revnum = pexec('hg','id','--num')
print('%s : %s (%s)' % (revnum, changeset, branch))

4

Dans Python 3.7, un nouvel argument de mot clé a capture_outputété introduit pour subprocess.run. Activation du court et simple:

import subprocess

p = subprocess.run("echo 'hello world!'", capture_output=True, shell=True, encoding="utf8")
assert p.stdout == 'hello world!\n'

1
import subprocess
output = str(subprocess.Popen("ntpq -p",shell = True,stdout = subprocess.PIPE, 
stderr = subprocess.STDOUT).communicate()[0])

Ceci est une solution en ligne


0

Ce qui suit capture stdout et stderr du processus dans une seule variable. Il est compatible Python 2 et 3:

from subprocess import check_output, CalledProcessError, STDOUT

command = ["ls", "-l"]
try:
    output = check_output(command, stderr=STDOUT).decode()
    success = True 
except CalledProcessError as e:
    output = e.output.decode()
    success = False

Si votre commande est une chaîne plutôt qu'un tableau, préfixez-la avec:

import shlex
command = shlex.split(command)

0

Pour python 3.5, j'ai mis en place une fonction basée sur la réponse précédente. Le journal peut être supprimé, je pense que c'est bien d'avoir

import shlex
from subprocess import check_output, CalledProcessError, STDOUT


def cmdline(command):
    log("cmdline:{}".format(command))
    cmdArr = shlex.split(command)
    try:
        output = check_output(cmdArr,  stderr=STDOUT).decode()
        log("Success:{}".format(output))
    except (CalledProcessError) as e:
        output = e.output.decode()
        log("Fail:{}".format(output))
    except (Exception) as e:
        output = str(e);
        log("Fail:{}".format(e))
    return str(output)


def log(msg):
    msg = str(msg)
    d_date = datetime.datetime.now()
    now = str(d_date.strftime("%Y-%m-%d %H:%M:%S"))
    print(now + " " + msg)
    if ("LOG_FILE" in globals()):
        with open(LOG_FILE, "a") as myfile:
            myfile.write(now + " " + msg + "\n")

0

Utiliser la ckeck_outputméthode desubprocess

import subprocess
address = 192.168.x.x
res = subprocess.check_output(['ping', address, '-c', '3'])

Enfin analyser la chaîne

for line in res.splitlines():

J'espère que cela aide, codage heureux

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.