Je recherche une solution Python qui me permettra de sauvegarder la sortie d'une commande dans un fichier sans la cacher de la console.
FYI: Je pose des questions sur tee (comme l'utilitaire de ligne de commande Unix) et non sur la fonction avec le même nom de Python module intertools.
tee
, elle n'est pas disponible sous Windows)stderr
et stdout
stderr = subprocess.STDOUT
ne fonctionnera pas.Voici quelques solutions incomplètes que j'ai trouvées jusqu'à présent:
Diagramme http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png
#!/usr/bin/python
from __future__ import print_function
import sys, os, time, subprocess, io, threading
cmd = "python -E test_output.py"
from threading import Thread
class StreamThread ( Thread ):
def __init__(self, buffer):
Thread.__init__(self)
self.buffer = buffer
def run ( self ):
while 1:
line = self.buffer.readline()
print(line,end="")
sys.stdout.flush()
if line == '':
break
proc = subprocess.Popen(cmd, Shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout))
stderrThread = StreamThread(io.TextIOWrapper(proc.stderr))
stdoutThread.start()
stderrThread.start()
proc.communicate()
stdoutThread.join()
stderrThread.join()
print("--done--")
#### test_output.py ####
#!/usr/bin/python
from __future__ import print_function
import sys, os, time
for i in range(0, 10):
if i%2:
print("stderr %s" % i, file=sys.stderr)
else:
print("stdout %s" % i, file=sys.stdout)
time.sleep(0.1)
stderr 1
stdout 0
stderr 3
stdout 2
stderr 5
stdout 4
stderr 7
stdout 6
stderr 9
stdout 8
--done--
La sortie escomptée devait être la commande des lignes. Remarque, la modification du Popen pour utiliser un seul PIPE n'est pas autorisée car dans la vraie vie, je voudrai faire différentes choses avec stderr et stdout.
Même dans le deuxième cas, je n'ai pas pu obtenir de sortie en temps réel, en fait, tous les résultats ont été reçus à la fin du processus. Par défaut, Popen ne doit utiliser aucun tampon (bufsize = 0).
Je vois que c'est un article plutôt ancien mais juste au cas où quelqu'un chercherait toujours un moyen de le faire:
proc = subprocess.Popen(["ping", "localhost"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
with open("logfile.txt", "w") as log_file:
while proc.poll() is None:
line = proc.stderr.readline()
if line:
print "err: " + line.strip()
log_file.write(line)
line = proc.stdout.readline()
if line:
print "out: " + line.strip()
log_file.write(line)
Enfin, j'ai dû implémenter la commande tee()
dans Python moi-même).
Vous pouvez l'obtenir ici http://github.com/pycontribs/tendo/blob/master/tendo/tee.py
Actuellement, cela vous permet de faire des choses comme:
tee("python --v") # works just like os.system()
tee("python --v", "log.txt") # file names
tee("python --v", file_handle)
import logging
tee("python --v", logging.info) # receive a method
La seule limitation actuelle est qu'il n'est pas en mesure de faire la différence entre stderr
et stdout
, ce qui signifie qu'il fusionnera les deux.
Il s'agit d'un port simple de tee
vers Python.
import sys
sinks = sys.argv[1:]
sinks = [open(sink, "w") for sink in sinks]
sinks.append(sys.stderr)
while True:
input = sys.stdin.read(1024)
if input:
for sink in sinks:
sink.write(input)
else:
break
Je suis sous Linux en ce moment mais cela devrait fonctionner sur la plupart des plateformes.
Maintenant, pour la partie subprocess
, je ne sais pas comment vous voulez "câbler" les stdin
, stdout
et stderr
du sous-processus à votre stdin
, stdout
, stderr
et les récepteurs de fichiers, mais je sais que vous pouvez le faire:
import subprocess
callee = subprocess.Popen( ["python", "-i"],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
Vous pouvez désormais accéder à callee.stdin
, callee.stdout
et callee.stderr
comme des fichiers normaux, ce qui permet la "solution" ci-dessus pour fonctionner. Si vous voulez obtenir le callee.returncode
, vous devrez faire un appel supplémentaire à callee.poll()
.
Soyez prudent lorsque vous écrivez dans callee.stdin
: Si le processus se termine lorsque vous faites cela, une erreur peut se produire (sous Linux, j'obtiens IOError: [Errno 32] Broken pipe
).
Il y a des problèmes/bogues subtils dans python lié au sous-processus.PIPE: http://bugs.python.org/issue1652
Apparemment, cela a été corrigé dans python3 +, mais pas dans python 2.7 et plus. Pour cela, vous devez utiliser: code.google.com/p/python-subprocess32/
Si vous ne voulez pas interagir avec le processus, vous pouvez très bien utiliser le module de sous-processus.
Exemple:
tester.py
import os
import sys
for file in os.listdir('.'):
print file
sys.stderr.write("Oh noes, a shrubbery!")
sys.stderr.flush()
sys.stderr.close()
testing.py
import subprocess
p = subprocess.Popen(['python', 'tester.py'], stdout=subprocess.PIPE,
stdin=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
print stdout, stderr
Dans votre situation, vous pouvez tout d'abord écrire stdout/stderr dans un fichier. Vous pouvez également envoyer des arguments à votre processus avec Communiquer, même si je n'ai pas réussi à comprendre comment interagir en permanence avec le sous-processus.
Voilà comment cela peut être fait
import sys
from subprocess import Popen, PIPE
with open('log.log', 'w') as log:
proc = Popen(["ping", "google.com"], stdout=PIPE, encoding='utf-8')
while proc.poll() is None:
text = proc.stdout.readline()
log.write(text)
sys.stdout.write(text)
J'ai écrit une chose qui encapsule les commandes Shell en Python.
Avantages clés:
Inconvénient clé:
source: https://Gist.github.com/AndrewHoos/9f03c74988469b517a7a
Essaye ça :
import sys
class tee-function :
def __init__(self, _var1, _var2) :
self.var1 = _var1
self.var2 = _var2
def __del__(self) :
if self.var1 != sys.stdout and self.var1 != sys.stderr :
self.var1.close()
if self.var2 != sys.stdout and self.var2 != sys.stderr :
self.var2.close()
def write(self, text) :
self.var1.write(text)
self.var2.write(text)
def flush(self) :
self.var1.flush()
self.var2.flush()
stderrsav = sys.stderr
out = open(log, "w")
sys.stderr = tee-function(stderrsav, out)
Ma solution n'est pas élégante, mais elle fonctionne.
Vous pouvez utiliser PowerShell pour accéder à "tee" sous WinOS.
import subprocess
import sys
cmd = ['powershell', 'ping', 'google.com', '|', 'tee', '-a', 'log.txt']
if 'darwin' in sys.platform:
cmd.remove('powershell')
p = subprocess.Popen(cmd)
p.wait()