333 votes

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

Comment puis-je obtenir la sortie d'un processus exécuté à l'aide de subprocess.call() ?

Passage d'un StringIO.StringIO à l'objet stdout donne cette erreur :

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/subprocess.py", line 444, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/subprocess.py", line 588, in __init__
    errread, errwrite) = self._get_handles(stdin, stdout, stderr)
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/subprocess.py", line 945, in _get_handles
    c2pwrite = stdout.fileno()
AttributeError: StringIO instance has no attribute 'fileno'
>>>

2 votes

La réponse de Mike est correcte. Notez que StringIO fonctionne comme un fichier dans la plupart des cas mais pas tous. Cela ne fonctionne pas dans votre cas parce que la multiprocessing suppose des fichiers réels dans certains cas. Ceci a peut-être été corrigé : voir bugs.python.org/issue5313 pour un bogue connexe.

0 votes

En fait, communicate() utilise select.select() qui n'accepte que les descripteurs de fichiers, ce n'est donc pas vraiment un bogue. J'étais assez confus par ce problème lorsque je l'ai rencontré pour la première fois et l'exploration des profondeurs de subprocess.py m'a beaucoup appris !

1 votes

Je pense subprocess.run rend cela plus simple, à partir de Python 3.5. J'ajouterai une réponse quand j'en aurai l'occasion.

340voto

Sergi Points 537

Si vous avez une version de Python >= 2.7, vous pouvez utiliser sous-processus.check_output qui fait exactement ce que vous voulez (il retourne la sortie standard sous forme de chaîne).

Exemple simple (version linux, voir note) :

import subprocess

print subprocess.check_output(["ping", "-c", "1", "8.8.8.8"])

Notez que la commande ping utilise la notation linux ( -c pour compter). Si vous essayez ceci sous Windows, n'oubliez pas de le changer en -n pour le même résultat.

Comme commenté ci-dessous, vous pouvez trouver une explication plus détaillée en cette autre réponse .

42 votes

J'ai trouvé une autre réponse avec un code fonctionnel . S'il vous plaît upvote si vous l'avez utilisé.

9 votes

Mais attention, check_output lancera une exception si le processus renvoie un code de sortie non nul.

0 votes

Notez que check_output souffre du problème de remplissage du PIPE comme run puisqu'il s'agit simplement d'appeler run . Donc, si vous avez un processus qui génère un peu plus de sortie, il sera suspendu indéfiniment. Le site Popen solution de @Mike's et @Jabba's les réponses fonctionnent beaucoup mieux

235voto

Mike Points 2037

Sortie de subprocess.call() ne doivent être redirigés que vers des fichiers.

Vous devez utiliser subprocess.Popen() à la place. Vous pouvez alors passer subprocess.PIPE pour les paramètres stderr, stdout, et/ou stdin et lire à partir des tuyaux en utilisant la commande communicate() méthode :

from subprocess import Popen, PIPE

p = Popen(['program', 'arg1'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
output, err = p.communicate(b"input data that is passed to subprocess' stdin")
rc = p.returncode

Le raisonnement est que l'objet de type fichier utilisé par subprocess.call() doit avoir un vrai descripteur de fichier, et donc implémenter la fonction fileno() méthode. L'utilisation de n'importe quel objet de type fichier ne suffira pas.

Voir ici pour plus d'informations.

60 votes

Cette page docs.python.org/library/subprocess.html#module-subprocess déconseille l'utilisation subprocess.PIPE Vous avez une idée de la façon de surmonter ce problème ?

6 votes

De plus, la question précise l'utilisation de subprocess.call et la réponse de Mike utilise Popen en fait, car subprocess.call ne renvoie que le code de retour, mais aucun moyen d'accéder aux flux. Si vous utilisez la version 2.6, vous pouvez utiliser la réponse de @Sergi si vous utilisez la version 2.7.

0 votes

Cela semble être une suggestion incorrecte...

106voto

Chiel92 Points 2362

Pour python 3.5+, il est recommandé d'utiliser la fonction exécuter la fonction du module de sous-processus . Cela renvoie un 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)

0 votes

C'est une réponse brillante. Mais n'oubliez pas la partie 'stdout=PIPE', comme je l'ai fait la première fois, sinon la sortie sera très courte !

3 votes

Au lieu de universal_newlines=True vous devez utiliser text=True car le premier est conservé pour des raisons de compatibilité ascendante.

5 votes

"Il est recommandé d'utiliser le run function" - cela ne fonctionne pas pour moi lorsque j'utilise ces paramètres, il semble que ce soit un problème avec Windows. Je l'ai résolu en ajoutant shell=True a la run commande ! Et quel que soit le système d'exploitation que vous utilisez, si vous avez Python 3.7+, remplacez la commande stdout=PIPE, stderr=PIPE con capture_output=True pour ajouter un tampon qui ne sera pas bloqué.

54voto

Jabba Points 1664

J'ai la solution suivante. Elle capture le code de sortie, le stdout, et le stderr aussi de la commande externe exécutée :

import shlex
from subprocess import Popen, PIPE

def get_exitcode_stdout_stderr(cmd):
    """
    Execute the external command and get its exitcode, stdout and stderr.
    """
    args = shlex.split(cmd)

    proc = Popen(args, stdout=PIPE, stderr=PIPE)
    out, err = proc.communicate()
    exitcode = proc.returncode
    #
    return exitcode, out, err

cmd = "..."  # arbitrary external command, e.g. "python mytest.py"
exitcode, out, err = get_exitcode_stdout_stderr(cmd)

J'ai également publié un article sur ce sujet sur mon blog ici .

Edit : la solution a été mise à jour vers une solution plus récente qui n'a pas besoin d'écrire dans les fichiers temporaires.

2 votes

@Jabba pour une raison quelconque, cela ne fonctionnait pas à moins que j'ajoute shell=True aux arguments de Popen(), pouvez-vous expliquer pourquoi ?

0 votes

@JaeGeeTee : Quelle est la commande que vous vouliez appeler ? Je suppose que vous vouliez appeler une commande qui contient des tuyaux (par exemple, " cat file.txt | wc -l ").

0 votes

Jabba, j'étais en train de courir. ping 127.0.0.1

31voto

Cheesemold Points 363

Je viens de comprendre comment faire, et voici un exemple de code tiré d'un de mes projets en cours :

#Getting the random picture.
#First find all pictures:
import shlex, subprocess
cmd = 'find ../Pictures/ -regex ".*\(JPG\|NEF\|jpg\)" '
#cmd = raw_input("shell:")
args = shlex.split(cmd)
output,error = subprocess.Popen(args,stdout = subprocess.PIPE, stderr= subprocess.PIPE).communicate()
#Another way to get output
#output = subprocess.Popen(args,stdout = subprocess.PIPE).stdout
ber = raw_input("search complete, display results?")
print output
#... and on to the selection process ...

Vous avez maintenant la sortie de la commande stockée dans la variable "output". "stdout = subprocess.PIPE" indique à la classe de créer un objet fichier nommé 'stdout' à partir de Popen. La méthode communicate(), d'après ce que je peux dire, agit simplement comme un moyen pratique de retourner un tuple de la sortie et des erreurs du processus que vous avez exécuté. De plus, le processus est lancé lors de l'instanciation de Popen.

0 votes

Ne répond pas vraiment à la question de savoir comment utiliser l'appel (judicieusement).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X