Frage

Ich versuche, einen Wrapper-Skript für ein Befehlszeilenprogramm (svnadmin überprüfen) zu schreiben, die einen schönen Fortschrittsanzeige für den Betrieb angezeigt werden. Dies erfordert mich in der Lage sein, jede Zeile der Ausgabe von dem umwickelten Programm zu sehen, sobald es ausgegeben wird.

dachte ich, dass ich nur das Programm mit subprocess.Popen ausführen würde, verwenden stdout=PIPE, dann lesen Sie jede Zeile, wie es in und handeln sie entsprechend kam. Allerdings, wenn ich den folgenden Code lief, wird der Ausgang schien irgendwo zwischengespeichert werden, so dass es in zwei Stücken erscheinen, Zeilen 1 bis 332, dann 333 bis 439 (die letzte Zeile der Ausgabe)

from subprocess import Popen, PIPE, STDOUT

p = Popen('svnadmin verify /var/svn/repos/config', stdout = PIPE, 
        stderr = STDOUT, shell = True)
for line in p.stdout:
    print line.replace('\n', '')

in der Dokumentation zu subprocess Nachdem ich ein wenig, entdeckte ich den bufsize Parameter Popen, also versuchte ich bufsize bis 1 (Puffer jede Zeile) und 0 (kein Puffer) Einstellung, aber weder Wert schien die Art und Weise die Linien zu ändern wurden ausgeliefert.

An diesem Punkt, den ich für Strohhalme zu begreifen begann, so schrieb ich die folgende Ausgabe Schleife:

while True:
    try:
        print p.stdout.next().replace('\n', '')
    except StopIteration:
        break

bekam aber das gleiche Ergebnis.

Ist es möglich zu erhalten ‚Realtime‘ Programm Ausgabe eines ausgeführten Programms mit subprocess? Gibt es eine andere Möglichkeit in Python, das voraus kompatibel ist (nicht exec*)?

War es hilfreich?

Lösung

Ich habe versucht, diese, und aus irgendeinem Grund während der Code

for line in p.stdout:
  ...

Puffer aggressiv, die Variante

while True:
  line = p.stdout.readline()
  if not line: break
  ...

nicht. Offensichtlich ist dies ein bekannter Fehler: http://bugs.python.org/issue3907 (Die Frage ist jetzt „geschlossen“ zum 29. August 2018)

Andere Tipps

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1)
for line in iter(p.stdout.readline, b''):
    print line,
p.stdout.close()
p.wait()

Sie können versuchen, diese:

import subprocess
import sys

process = subprocess.Popen(
    cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)

while True:
    out = process.stdout.read(1)
    if out == '' and process.poll() != None:
        break
    if out != '':
        sys.stdout.write(out)
        sys.stdout.flush()

Wenn Sie Readline- statt Lese verwenden, wird es einige Fälle, in denen die Eingangsnachricht wird nicht gedruckt. Versuchen Sie es mit einem Befehl die erfordert eine Inline-Eingabe und überzeugen Sie sich selbst.

Sie können direkt die subprocess Ausgabe an die Ströme lenken. Vereinfachtes Beispiel:

subprocess.run(['ls'], stderr=sys.stderr, stdout=sys.stdout)

Ich lief in das gleiche Problem eine Weile zurück. Meine Lösung war für die read Methode zu Graben laufen, die sofort selbst zurück, wenn Ihr subprocess nicht fertig ausgeführt wird, etc.

Echtzeit Output Problembehebung: Ich habe ähnliches Problem in Python aufgetreten, während die Echtzeit-Ausgabe von c-Programm zu erfassen. Ich fügte hinzu, " fflush (stdout) ;" in meinem C-Code. Er arbeitete für mich. Hier ist der Schnipsel der Code

<< C-Programm >>

#include <stdio.h>
void main()
{
    int count = 1;
    while (1)
    {
        printf(" Count  %d\n", count++);
        fflush(stdout);
        sleep(1);
    }
}

<< Python-Programm >>

#!/usr/bin/python

import os, sys
import subprocess


procExe = subprocess.Popen(".//count", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

while procExe.poll() is None:
    line = procExe.stdout.readline()
    print("Print:" + line)

<< >> OUTPUT Print: Count 1 Print: Count 2 Druck: Anzahl 3

Hoffe, es hilft.

~ sairam

Sie können einen Iterator über jedes Byte in der Ausgabe des Teilprozesses verwenden. Dies ermöglicht Inline-Update (Linien mit der Endung '\ r' vorherige Ausgangsleitung überschreibt) von der subprocess:

from subprocess import PIPE, Popen

command = ["my_command", "-my_arg"]

# Open pipe to subprocess
subprocess = Popen(command, stdout=PIPE, stderr=PIPE)


# read each byte of subprocess
while subprocess.poll() is None:
    for c in iter(lambda: subprocess.stdout.read(1) if subprocess.poll() is None else {}, b''):
        c = c.decode('ascii')
        sys.stdout.write(c)
sys.stdout.flush()

if subprocess.returncode != 0:
    raise Exception("The subprocess did not terminate correctly.")

Je nach Anwendungsfall, können Sie auch die Pufferung im subprocess deaktivieren möchten selbst.

Wenn die Subprozess einen Python-Prozess sein, könnten Sie dies tun vor dem Aufruf:

os.environ["PYTHONUNBUFFERED"] = "1"

oder alternativ passieren diese im env Argumente Popen.

Andernfalls, wenn Sie unter Linux / Unix sind, können Sie das stdbuf-Tool verwenden. Z.B. wie:

cmd = ["stdbuf", "-oL"] + cmd

Siehe auch hier über stdbuf oder andere Optionen.

(Siehe auch hier für die gleiche Antwort.)

Die Streaming subprocess stdin und stdout mit asyncio in Python Blog-Eintrag von Kevin McCarthy zeigt, wie zu tun es mit asyncio:

import asyncio
from asyncio.subprocess import PIPE
from asyncio import create_subprocess_exec


async def _read_stream(stream, callback):
    while True:
        line = await stream.readline()
        if line:
            callback(line)
        else:
            break


async def run(command):
    process = await create_subprocess_exec(
        *command, stdout=PIPE, stderr=PIPE
    )

    await asyncio.wait(
        [
            _read_stream(
                process.stdout,
                lambda x: print(
                    "STDOUT: {}".format(x.decode("UTF8"))
                ),
            ),
            _read_stream(
                process.stderr,
                lambda x: print(
                    "STDERR: {}".format(x.decode("UTF8"))
                ),
            ),
        ]
    )

    await process.wait()


async def main():
    await run("docker build -t my-docker-image:latest .")


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Mit pexpect [ http://www.noah.org/wiki/Pexpect ] mit nicht-blockierende Leseleitungen werden dieses Problem beheben. Es ergibt sich aus der Tatsache, dass Rohre gepuffert werden, und so Ausgabe der App wird durch das Rohr gepuffert zu werden, damit Sie nicht zu dieser Ausgabe erhalten kann, bis der Puffer voll ist oder der Prozess stirbt.

habe ich diese Lösung Echtzeit-Ausgabe auf einem Teilprozess zu bekommen. Diese Schleife wird beendet, sobald der Prozess abgeschlossen ist eine Notwendigkeit für eine break-Anweisung oder mögliche Endlosschleife Weglassen.

sub_process = subprocess.Popen(my_command, close_fds=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

while sub_process.poll() is None:
    out = sub_process.stdout.read(1)
    sys.stdout.write(out)
    sys.stdout.flush()

Sie haben diese "Plug-and-Play" -Funktion hier . Arbeitete wie ein Charme!

import subprocess

def myrun(cmd):
    """from http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html
    """
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    stdout = []
    while True:
        line = p.stdout.readline()
        stdout.append(line)
        print line,
        if line == '' and p.poll() != None:
            break
    return ''.join(stdout)

Komplettlösung:

import contextlib
import subprocess

# Unix, Windows and old Macintosh end-of-line
newlines = ['\n', '\r\n', '\r']
def unbuffered(proc, stream='stdout'):
    stream = getattr(proc, stream)
    with contextlib.closing(stream):
        while True:
            out = []
            last = stream.read(1)
            # Don't loop forever
            if last == '' and proc.poll() is not None:
                break
            while last not in newlines:
                # Don't loop forever
                if last == '' and proc.poll() is not None:
                    break
                out.append(last)
                last = stream.read(1)
            out = ''.join(out)
            yield out

def example():
    cmd = ['ls', '-l', '/']
    proc = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        # Make all end-of-lines '\n'
        universal_newlines=True,
    )
    for line in unbuffered(proc):
        print line

example()

Dies ist das Grundgerüst, das ich immer für diesen. Es macht es einfach Timeouts zu implementieren und ist in der Lage mit dem unvermeidlichen hängenden Prozessen zu befassen.

import subprocess
import threading
import Queue

def t_read_stdout(process, queue):
    """Read from stdout"""

    for output in iter(process.stdout.readline, b''):
        queue.put(output)

    return

process = subprocess.Popen(['dir'],
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT,
                           bufsize=1,
                           cwd='C:\\',
                           shell=True)

queue = Queue.Queue()
t_stdout = threading.Thread(target=t_read_stdout, args=(process, queue))
t_stdout.daemon = True
t_stdout.start()

while process.poll() is None or not queue.empty():
    try:
        output = queue.get(timeout=.5)

    except Queue.Empty:
        continue

    if not output:
        continue

    print(output),

t_stdout.join()

(Diese Lösung wurde mit Python 2.7.15 getestet)
Sie müssen nur sys.stdout.flush () nach jeder Zeile Lesen / Schreiben:

while proc.poll() is None:
    line = proc.stdout.readline()
    sys.stdout.write(line)
    # or print(line.strip()), you still need to force the flush.
    sys.stdout.flush()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top