J'utilise Popen pour appeler un script Shell qui écrit continuellement ses stdout et stderr dans un fichier journal. Existe-t-il un moyen de sortir simultanément le fichier journal en continu (à l'écran), ou de faire en sorte que le script Shell écrive à la fois dans le fichier journal et stdout en même temps?
Je veux essentiellement faire quelque chose comme ça en Python:
cat file 2>&1 | tee -a logfile #"cat file" will be replaced with some script
Encore une fois, ces tuyaux stderr/stdout ensemble pour tee, ce qui l'écrit à la fois sur stdout et mon fichier journal.
Je sais comment écrire stdout et stderr dans un fichier journal en Python. Là où je suis coincé, c'est comment les reproduire à l'écran:
subprocess.Popen("cat file", Shell=True, stdout=logfile, stderr=logfile)
Bien sûr, je pourrais simplement faire quelque chose comme ça, mais y a-t-il un moyen de le faire sans redirection des descripteurs de fichiers Shell et Shell?:
subprocess.Popen("cat file 2>&1 | tee -a logfile", Shell=True)
Vous pouvez utiliser un canal pour lire les données de la sortie standard du programme et les écrire à tous les endroits que vous souhaitez:
import sys
import subprocess
logfile = open('logfile', 'w')
proc=subprocess.Popen(['cat', 'file'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in proc.stdout:
sys.stdout.write(line)
logfile.write(line)
proc.wait()
MISE À JOUR
Dans python 3, le paramètre universal_newlines
Contrôle la façon dont les tuyaux sont utilisés. Si False
, les lectures de tuyau renvoient les objets bytes
et peuvent avoir besoin d'être décodé (par exemple, line.decode('utf-8')
) pour obtenir une chaîne. Si True
, python fait le décodage pour vous
Modifié dans la version 3.3: Lorsque universal_newlines a la valeur True, la classe utilise l'encodage locale.getpreferredencoding (False) au lieu de locale.getpreferredencoding (). Voir la classe io.TextIOWrapper pour plus d'informations sur cette modification.
Pour émuler: subprocess.call("command 2>&1 | tee -a logfile", Shell=True)
sans appeler la commande tee
:
#!/usr/bin/env python2
from subprocess import Popen, PIPE, STDOUT
p = Popen("command", stdout=PIPE, stderr=STDOUT, bufsize=1)
with p.stdout, open('logfile', 'ab') as file:
for line in iter(p.stdout.readline, b''):
print line, #NOTE: the comma prevents duplicate newlines (softspace hack)
file.write(line)
p.wait()
Pour résoudre les éventuels problèmes de mise en mémoire tampon (si la sortie est retardée), voir les liens dans Python: lire l'entrée de streaming depuis subprocess.communicate () .
Voici Python 3 version:
#!/usr/bin/env python3
import sys
from subprocess import Popen, PIPE, STDOUT
with Popen("command", stdout=PIPE, stderr=STDOUT, bufsize=1) as p, \
open('logfile', 'ab') as file:
for line in p.stdout: # b'\n'-separated lines
sys.stdout.buffer.write(line) # pass bytes as is
file.write(line)
Ecriture octet par octet sur le terminal pour les applications interactives
Cette méthode écrit immédiatement tous les octets qu'elle obtient sur stdout, ce qui simule de plus près le comportement de tee
, en particulier pour les applications interactives.
main.py
#!/usr/bin/env python3
import os
import subprocess
import sys
with subprocess.Popen(sys.argv[1:], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc, \
open('logfile.txt', 'bw') as logfile:
while True:
byte = proc.stdout.read(1)
if byte:
sys.stdout.buffer.write(byte)
sys.stdout.flush()
logfile.write(byte)
# logfile.flush()
else:
break
exit_status = proc.returncode
sleep.py
#!/usr/bin/env python3
import sys
import time
for i in range(10):
print(i)
sys.stdout.flush()
time.sleep(1)
D'abord, nous pouvons faire un bilan de santé non interactif:
./main.py ./sleep.py
Et nous le voyons compter sur la sortie en temps réel.
Ensuite, pour un test interactif, vous pouvez exécuter:
./main.py bash
Ensuite, les caractères que vous tapez apparaissent immédiatement sur le terminal lorsque vous les tapez, ce qui est très important pour les applications interactives. Voici ce qui se passe lorsque vous exécutez:
bash | tee logfile.txt
De plus, si vous souhaitez que la sortie s'affiche immédiatement dans le fichier ouptut, vous pouvez également ajouter un:
logfile.flush()
mais tee
ne fait pas cela, et je crains que cela ne tue les performances. Vous pouvez le tester facilement avec:
tail -f logfile.txt
Question connexe: sortie en direct de la commande de sous-processus
Testé sur Ubuntu 18.04, Python 3.6.7.