Ich möchte subprocess.Popen()
rsync.exe in Windows und die Standardausgabe in Python drucken.
Mein Code funktioniert, aber er erfasst den Fortschritt erst, wenn eine Dateiübertragung abgeschlossen ist! Ich möchte den Fortschritt für jede Datei in Echtzeit drucken.
Verwenden Sie Python 3.1 jetzt, da ich gehört habe, dass es besser mit E / A umgehen kann.
import subprocess, time, os, sys
cmd = "rsync.exe -vaz -P source/ dest/"
p, line = True, 'start'
p = subprocess.Popen(cmd,
shell=True,
bufsize=64,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
for line in p.stdout:
print(">>> " + str(line.rstrip()))
p.stdout.flush()
python
subprocess
stdout
John A.
quelle
quelle
Antworten:
Einige Faustregeln für
subprocess
.shell=True
. Es ruft unnötig einen zusätzlichen Shell-Prozess auf, um Ihr Programm aufzurufen.sys.argv
in Python ist eine Liste, ebenso wieargv
in C. Sie übergeben also eine Liste ,Popen
um Unterprozesse aufzurufen, keine Zeichenfolge.stderr
zu a um,PIPE
wenn Sie es nicht lesen.stdin
wenn Sie nicht darauf schreiben.Beispiel:
Das heißt, es ist wahrscheinlich, dass rsync seinen Ausgang puffert, wenn es erkennt, dass es mit einer Pipe anstelle eines Terminals verbunden ist. Dies ist das Standardverhalten. Wenn eine Verbindung zu einer Pipe besteht, müssen Programme stdout explizit leeren, um Echtzeitergebnisse zu erhalten. Andernfalls wird die Standard-C-Bibliothek gepuffert.
Um dies zu testen, führen Sie stattdessen Folgendes aus:
und erstellen Sie eine
test_out.py
Datei mit dem Inhalt:Wenn Sie diesen Unterprozess ausführen, sollten Sie "Hallo" erhalten und 10 Sekunden warten, bevor Sie "Welt" geben. Wenn dies mit dem obigen Python-Code und nicht mit passiert
rsync
, bedeutet dies, dass diersync
Ausgabe selbst gepuffert wird, sodass Sie kein Glück haben.Eine Lösung wäre
pty
, sich mit so etwas direkt mit a zu verbindenpexpect
.quelle
shell=False
ist richtig, wenn Sie eine Befehlszeile erstellen, insbesondere aus vom Benutzer eingegebenen Daten. Diesshell=True
ist jedoch auch nützlich, wenn Sie die gesamte Befehlszeile von einer vertrauenswürdigen Quelle erhalten (z. B. im Skript fest codiert).shell=True
. Denken Sie darüber nach - Sie rufen einen anderen Prozess auf Ihrem Betriebssystem auf, der Speicherzuweisung, Festplattennutzung und Prozessorplanung umfasst, nur um eine Zeichenfolge zu teilen ! Und einer, den du dir angeschlossen hast !! Sie könnten in Python aufteilen, aber es ist trotzdem einfacher, jeden Parameter einzeln zu schreiben. Auch eine Liste Mittel verwenden Sie müssen keine spezielle Shell - Zeichen zu entkommen: Räume;
,>
,<
,&
.. Ihre Parameter können diese Zeichen enthalten und Sie müssen sich keine Sorgen machen! Ich kann keinen Grund für die Verwendung erkennenshell=True
, es sei denn, Sie führen einen Nur-Shell-Befehl aus.csv
Modul verwenden. Als Beispiel wäre Ihre Pipeline in Python jedoch:p = Popen(['cut', '-f1'], stdin=open('longfile.tab'), stdout=PIPE) ; p2 = Popen(['head', '-100'], stdin=p.stdout, stdout=PIPE) ; result, stderr = p2.communicate() ; print result
Beachten Sie, dass Sie mit langen Dateinamen und Shell-Sonderzeichen arbeiten können, ohne entkommen zu müssen, da die Shell nicht beteiligt ist. Es ist auch viel schneller, da es einen Prozess weniger gibt.for line in iter(p.stdout.readline, b'')
stattfor line in p.stdout
in Python 2, da sonst Zeilen nicht in Echtzeit gelesen werden, auch wenn der Quellprozess seine Ausgabe nicht puffert.Ich weiß, dass dies ein altes Thema ist, aber jetzt gibt es eine Lösung. Rufen Sie den rsync mit der Option --outbuf = L auf. Beispiel:
quelle
preexec_fn=os.setpgrp
, damit das Programm sein übergeordnetes Skript überlebt 2. Sie das Lesen aus der Pipe des Prozesses überspringen 3. der Prozess viele Daten ausgibt und die Pipe füllt 4. Sie stundenlang stecken bleiben und versuchen herauszufinden, warum das Programm, das Sie ausführen, nach einer zufälligen Zeit beendet wird . Die Antwort von @nosklo hat mir sehr geholfen.Unter Linux hatte ich das gleiche Problem, die Pufferung zu beseitigen. Ich habe schließlich "stdbuf -o0" (oder, erwartungsgemäß nicht gepuffert) verwendet, um die PIPE-Pufferung zu entfernen.
Ich könnte dann select.select auf stdout verwenden.
Siehe auch /unix/25372/
quelle
stdbuf -o0
als sehr nützlich erwiesen. Ich habe geschrieben, dass eine C ++ - App erzeugt und überprüft wird, ob bestimmte Protokollanweisungen ausgegeben werden. Ohnestdbuf -o0
diese Tests wurden 7 Sekunden benötigt, um die (gepufferte) Ausgabe des C ++ - Programms zu erhalten. Jetzt rennen sie fast augenblicklich!blockiert immer bis zum nächsten Zeilenvorschub.
Für "Echtzeit" -Verhalten müssen Sie Folgendes tun:
Die while-Schleife bleibt erhalten, wenn der untergeordnete Prozess seine Standardausgabe schließt oder beendet.
read()/read(-1)
würde blockieren, bis der untergeordnete Prozess seine Standardausgabe geschlossen oder beendet hat.quelle
inchar
wird stattdessen nieNone
verwendetif not inchar:
(read()
gibt eine leere Zeichenfolge in EOF zurück). Übrigens ist es schlimmer,for line in p.stdout
dass in Python 2 nicht einmal vollständige Zeilen in Echtzeit gedruckt werdenfor line in
(stattdessen könnte iter (p.stdout.readline, '') `verwendet werden).for line in p.stdout:
funktioniert unter Python 3. Stellen Sie sicher, dass Sie den Unterschied zwischen''
(Unicode-Zeichenfolge) undb''
(Bytes) verstehen . Siehe Python: Lesen Sie die Streaming-Eingabe von subprocess.communicate ()Je nach Anwendungsfall möchten Sie möglicherweise auch die Pufferung im Unterprozess selbst deaktivieren.
Wenn der Unterprozess ein Python-Prozess ist, können Sie dies vor dem Aufruf tun:
Oder übergeben Sie dies alternativ im
env
Argument anPopen
.Andernfalls können Sie unter Linux / Unix das
stdbuf
Tool verwenden. ZB wie:Siehe auch hier über
stdbuf
oder andere Optionen.quelle
Ihr Problem ist:
Der Iterator selbst verfügt über eine zusätzliche Pufferung.
Versuchen Sie es so:
quelle
Sie können stdout nicht dazu bringen, ungepuffert in eine Pipe zu drucken (es sei denn, Sie können das Programm, das in stdout druckt, neu schreiben). Hier ist meine Lösung:
Leiten Sie stdout zu sterr um, das nicht gepuffert ist.
'<cmd> 1>&2'
Sollte es tun. Öffnen Sie den Prozess wie folgt:myproc = subprocess.Popen('<cmd> 1>&2', stderr=subprocess.PIPE)
Sie können nicht von stdout oder stderr unterscheiden, aber Sie erhalten alle Ausgaben sofort.
Ich hoffe, dies hilft jedem, dieses Problem anzugehen.
quelle
1>&2
ändert lediglich, auf welche Dateien die Dateideskriptoren verweisen, bevor das Programm gestartet wird. Das Programm selbst kann nicht zwischen der Umleitung von stdout nach stderr (1>&2
) oder umgekehrt (2>&1
) unterscheiden, sodass dies keine Auswirkungen auf das Pufferverhalten des Programms hat. In beiden Fällen wird die1>&2
Syntax von der Shell interpretiert.subprocess.Popen('<cmd> 1>&2', stderr=subprocess.PIPE)
würde fehlschlagen, weil Sie nicht angegeben habenshell=True
.Ändern Sie die Standardausgabe des rsync-Prozesses so, dass sie nicht gepuffert wird.
quelle
Um das Zwischenspeichern der Ausgabe zu vermeiden, sollten Sie pexpect ausprobieren.
PS : Ich weiß, dass diese Frage ziemlich alt ist und immer noch die Lösung bietet, die für mich funktioniert hat.
PPS : Ich habe diese Antwort von einer anderen Frage erhalten
quelle
Ich schreibe eine GUI für rsync in Python und habe die gleichen Probleme. Dieses Problem hat mich mehrere Tage lang beunruhigt, bis ich es in pyDoc finde.
Es scheint, dass rsync '\ r' ausgibt, wenn die Übersetzung ausgeführt wird.
quelle
Ich habe festgestellt, dass die Verwendung einer temporären Datei als Zwischenprodukt nicht erwähnt wird. Im Folgenden werden die Pufferungsprobleme durch Ausgabe in eine temporäre Datei umgangen und Sie können die von rsync kommenden Daten analysieren, ohne eine Verbindung zu einer pty herzustellen. Ich habe Folgendes auf einer Linux-Box getestet, und die Ausgabe von rsync unterscheidet sich tendenziell zwischen den Plattformen, sodass die regulären Ausdrücke zum Analysieren der Ausgabe variieren können:
quelle
while not p.poll()
führt zu einer Endlosschleife, wenn der Unterprozess mit 0 erfolgreich beendet wird. Verwenden Siep.poll() is None
stattdessenopen(file_name)
command_argv = ["stdbuf","-i0","-o0","-e0"] + command_argv
und rufe auf:popen = subprocess.Popen(cmd, stdout=subprocess.PIPE)
und jetzt kann ich ohne Pufferung auslesenWenn Sie so etwas in einem Thread ausführen und die Eigenschaft ffmpeg_time in einer Eigenschaft einer Methode speichern, damit Sie darauf zugreifen können, funktioniert dies sehr gut. Ich erhalte folgende Ausgaben: Die Ausgabe ist so, als ob Sie Threading in tkinter verwenden
quelle
In Python 3 gibt es eine Lösung, die einen Befehl von der Befehlszeile entfernt und beim Empfang gut dekodierte Zeichenfolgen in Echtzeit liefert.
Empfänger (
receiver.py
):Beispiel eines einfachen Programms, das eine Echtzeitausgabe erzeugen könnte (
dummy_out.py
):Ausgabe:
quelle