Duplicata possible:
Envelopper le sous-processus stdout/stderr
Dans cette question , hanan-n a demandé s'il était possible d'avoir un sous-processus python qui sort en sortie standard tout en conservant la sortie dans une chaîne pour un traitement ultérieur. La solution dans ce cas était de boucler sur chaque ligne de sortie et de les imprimer manuellement:
output = []
p = subprocess.Popen(["the", "command"], stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, ''):
print(line)
output.append(line)
Cependant, cette solution ne se généralise pas dans le cas où vous voulez le faire pour stdout et stderr, tout en satisfaisant les points suivants:
J'ai parcouru la documentation du sous-processus , mais je n'ai rien trouvé qui puisse y parvenir. Le plus proche que j'ai pu trouver est d'ajouter stderr=subprocess.stdout
et utiliser la même solution que ci-dessus, mais nous perdons alors la distinction entre sortie régulière et erreurs. Des idées? Je suppose que la solution - s'il y en a une - impliquera d'avoir des lectures asynchrones sur p.stdout
et p.stderr
.
Voici un exemple de ce que j'aimerais faire:
p = subprocess.Popen(["the", "command"])
p.wait() # while p runs, the command's stdout and stderr should behave as usual
p_stdout = p.stdout.read() # unfortunately, this will return '' unless you use subprocess.PIPE
p_stderr = p.stderr.read() # ditto
[do something with p_stdout and p_stderr]
Cet exemple semble fonctionner pour moi:
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import subprocess
import sys
import select
p = subprocess.Popen(["find", "/proc"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = []
stderr = []
while True:
reads = [p.stdout.fileno(), p.stderr.fileno()]
ret = select.select(reads, [], [])
for fd in ret[0]:
if fd == p.stdout.fileno():
read = p.stdout.readline()
sys.stdout.write('stdout: ' + read)
stdout.append(read)
if fd == p.stderr.fileno():
read = p.stderr.readline()
sys.stderr.write('stderr: ' + read)
stderr.append(read)
if p.poll() != None:
break
print 'program ended'
print 'stdout:', "".join(stdout)
print 'stderr:', "".join(stderr)
En général, dans toute situation où vous voulez faire des trucs avec plusieurs descripteurs de fichiers en même temps et vous ne savez pas lequel aura des trucs à lire, vous devez utiliser select ou quelque chose d'équivalent (comme un réacteur Twisted).
Pour imprimer sur la console et capturer dans une chaîne stdout/stderr d'un sous-processus de manière portable:
from StringIO import StringIO
fout, ferr = StringIO(), StringIO()
exitcode = teed_call(["the", "command"], stdout=fout, stderr=ferr)
stdout = fout.getvalue()
stderr = ferr.getvalue()
où teed_call()
est défini dans le sous-processus Python récupère la sortie des enfants dans le fichier et le terminal?
Vous pouvez utiliser n'importe quel objet de type fichier (méthode .write()
).
Créez deux lecteurs comme ci-dessus, un pour stdout
un pour stderr
et démarrez chacun dans un nouveau thread. Cela s'ajouterait à la liste dans à peu près le même ordre de sortie que par le processus. Maintenez deux listes distinctes si vous le souhaitez.
c'est à dire.,
p = subprocess.Popen(["the", "command"])
t1 = thread.start_new_thread(func,stdout) # create a function with the readers
t2 = thread.start_new_thread(func,stderr)
p.wait()
# your logic here