Ich habe einige Erfolge bei der Lösung meines Problems erzielt. Hier sind die Details mit einigen Erklärungen, falls jemand mit einem ähnlichen Problem diese Seite findet. Wenn Sie sich jedoch nicht für Details interessieren, finden Sie hier die kurze Antwort :
Verwenden Sie PTY.spawn auf folgende Weise (natürlich mit Ihrem eigenen Befehl):
require 'pty'
cmd = "blender -b mball.blend -o //renders/ -F JPEG -x 1 -f 1"
begin
PTY.spawn( cmd ) do |stdout, stdin, pid|
begin
# Do stuff with the output here. Just printing to show it works
stdout.each { |line| print line }
rescue Errno::EIO
puts "Errno:EIO error, but this probably just means " +
"that the process has finished giving output"
end
end
rescue PTY::ChildExited
puts "The child process exited!"
end
Und hier ist die lange Antwort mit viel zu vielen Details:
Das eigentliche Problem scheint zu sein, dass, wenn ein Prozess seine Standardausgabe nicht explizit löscht, alles, was in Standardausgabe geschrieben wird, gepuffert und nicht tatsächlich gesendet wird, bis der Prozess abgeschlossen ist, um E / A zu minimieren (dies ist anscheinend ein Implementierungsdetail von vielen C-Bibliotheken, die so erstellt wurden, dass der Durchsatz durch weniger häufige E / A maximiert wird. Wenn Sie den Prozess einfach so ändern können, dass er regelmäßig gelöscht wird, ist dies Ihre Lösung. In meinem Fall war es ein Mixer, also ein bisschen einschüchternd für einen kompletten Noob wie mich, die Quelle zu ändern.
Wenn Sie diese Prozesse jedoch über die Shell ausführen, wird stdout in Echtzeit für die Shell angezeigt, und stdout scheint nicht gepuffert zu sein. Es wird nur gepuffert, wenn es von einem anderen Prozess aufgerufen wird, aber wenn eine Shell behandelt wird, wird das Standardout in Echtzeit ungepuffert angezeigt.
Dieses Verhalten kann sogar bei einem Ruby-Prozess als untergeordneter Prozess beobachtet werden, dessen Ausgabe in Echtzeit erfasst werden muss. Erstellen Sie einfach ein Skript, random.rb, mit der folgenden Zeile:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
Dann ein Ruby-Skript, um es aufzurufen und seine Ausgabe zurückzugeben:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
Sie werden sehen, dass Sie das Ergebnis nicht wie erwartet in Echtzeit erhalten, sondern auf einmal danach. STDOUT wird gepuffert, obwohl es nicht gepuffert wird, wenn Sie random.rb selbst ausführen. Dies kann gelöst werden, indem eine STDOUT.flush
Anweisung innerhalb des Blocks in random.rb hinzugefügt wird. Wenn Sie die Quelle jedoch nicht ändern können, müssen Sie dies umgehen. Sie können es nicht von außerhalb des Prozesses spülen.
Wenn der Unterprozess in Echtzeit in eine Shell gedruckt werden kann, muss es eine Möglichkeit geben, dies auch mit Ruby in Echtzeit zu erfassen. Und da ist. Sie müssen das PTY-Modul verwenden, das meiner Meinung nach im Ruby Core enthalten ist (1.8.6 sowieso). Traurige Sache ist, dass es nicht dokumentiert ist. Aber ich habe zum Glück einige Anwendungsbeispiele gefunden.
Um zu erklären, was PTY ist, steht es zunächst für Pseudo-Terminal . Grundsätzlich kann sich das Ruby-Skript dem Unterprozess so präsentieren, als wäre es ein echter Benutzer, der den Befehl gerade in eine Shell eingegeben hat. Daher tritt jedes geänderte Verhalten auf, das nur auftritt, wenn ein Benutzer den Prozess über eine Shell gestartet hat (z. B. wenn STDOUT in diesem Fall nicht gepuffert wird). Wenn Sie die Tatsache verbergen, dass ein anderer Prozess diesen Prozess gestartet hat, können Sie STDOUT in Echtzeit erfassen, da es nicht gepuffert wird.
Versuchen Sie den folgenden Code, damit dies mit dem Skript random.rb als Kind funktioniert:
require 'pty'
begin
PTY.spawn( "ruby random.rb" ) do |stdout, stdin, pid|
begin
stdout.each { |line| print line }
rescue Errno::EIO
end
end
rescue PTY::ChildExited
puts "The child process exited!"
end
STDOUT.sync = true
ist alles, was benötigt wird (Mveermans Antwort unten). Hier ist ein weiterer Thread mit einem Beispielcode .verwenden
IO.popen
. Dies ist ein gutes Beispiel.Ihr Code würde so etwas wie:
quelle
yes
einer Befehlszeilenanwendung getestet, die niemals endet , und es hat funktioniert. Der Code war wie folgt :IO.popen('yes') { |p| p.each { |f| puts f } }
. Ich vermute, es hat etwas mit Mixer zu tun und nicht mit Rubin. Wahrscheinlich spült der Mixer seinen STDOUT nicht immer.STDOUT.flush oder STDOUT.sync = true
quelle
STDOUT.sync = true; system('<whatever-command>')
Blender druckt wahrscheinlich keine Zeilenumbrüche, bis das Programm beendet ist. Stattdessen wird das Wagenrücklaufzeichen (\ r) gedruckt. Die einfachste Lösung ist wahrscheinlich die Suche nach der magischen Option, mit der Zeilenumbrüche mit der Fortschrittsanzeige gedruckt werden.
Das Problem ist, dass
IO#gets
(und verschiedene andere E / A-Methoden) den Zeilenumbruch als Trennzeichen verwenden. Sie lesen den Stream, bis sie das Zeichen "\ n" treffen (der Mixer sendet nicht).Versuchen Sie, das Eingabetrennzeichen einzustellen
$/ = "\r"
oderblender.gets("\r")
stattdessen zu verwenden.Übrigens sollten Sie bei solchen Problemen immer überprüfen
puts someobj.inspect
oderp someobj
(beide tun dasselbe), um versteckte Zeichen in der Zeichenfolge zu sehen.quelle
Ich weiß nicht, ob ehsanul zu der Zeit die Frage beantwortet hat, es war noch
Open3::pipeline_rw()
verfügbar, aber es macht die Dinge wirklich einfacher.Ich verstehe ehsanuls Job bei Blender nicht, also habe ich mit
tar
und ein weiteres Beispiel gemachtxz
.tar
fügt Eingabedateien zum stdout-Stream hinzu,xz
nimmt diesestdout
und komprimiert sie erneut auf einen anderen stdout. Unsere Aufgabe ist es, den letzten Standard zu nehmen und in unsere endgültige Datei zu schreiben:quelle
Alte Frage, hatte aber ähnliche Probleme.
Ohne meinen Ruby-Code wirklich zu ändern, war eine Sache, die half, meine Pipe mit stdbuf zu verpacken , wie folgt :
In meinem Beispiel lautet der eigentliche Befehl, mit dem ich wie mit einer Shell interagieren möchte, openssl .
-oL -eL
Sagen Sie ihm, er soll STDOUT und STDERR nur bis zu einer neuen Zeile puffern. Ersetzen SieL
durch,0
um vollständig zu entpuffern.Dies funktioniert jedoch nicht immer: Manchmal erzwingt der Zielprozess seinen eigenen Stream-Puffertyp, wie in einer anderen Antwort angegeben.
quelle