J'ai réussi à résoudre mon problème. Voici les détails, avec quelques explications, au cas où quelqu'un ayant un problème similaire trouverait cette page. Mais si vous ne vous souciez pas des détails, voici la réponse courte :
Utilisez PTY.spawn de la manière suivante (avec votre propre commande bien sûr):
require 'pty'
cmd = "blender -b mball.blend -o //renders/ -F JPEG -x 1 -f 1"
begin
PTY.spawn( cmd ) do |stdout, stdin, pid|
begin
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
Et voici la réponse longue , avec beaucoup trop de détails:
Le vrai problème semble être que si un processus ne vide pas explicitement son stdout, alors tout ce qui est écrit sur stdout est mis en mémoire tampon plutôt que réellement envoyé, jusqu'à ce que le processus soit terminé, afin de minimiser les E / S (c'est apparemment un détail d'implémentation de beaucoup Bibliothèques C, conçues pour que le débit soit maximisé grâce à des E / S moins fréquentes). Si vous pouvez facilement modifier le processus pour qu'il vide régulièrement stdout, alors ce serait votre solution. Dans mon cas, c'était blender, donc un peu intimidant pour un noob complet comme moi de modifier la source.
Mais lorsque vous exécutez ces processus depuis le shell, ils affichent stdout vers le shell en temps réel, et le stdout ne semble pas être mis en mémoire tampon. Il est uniquement mis en mémoire tampon lorsqu'il est appelé depuis un autre processus, je crois, mais si un shell est traité, la sortie standard est vue en temps réel, sans tampon.
Ce comportement peut même être observé avec un processus ruby comme le processus enfant dont la sortie doit être collectée en temps réel. Créez simplement un script, random.rb, avec la ligne suivante:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
Puis un script ruby pour l'appeler et renvoyer sa sortie:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
Vous verrez que vous n'obtenez pas le résultat en temps réel comme vous pourriez vous y attendre, mais tout d'un coup par la suite. STDOUT est mis en mémoire tampon, même si vous exécutez vous-même random.rb, il ne l'est pas. Cela peut être résolu en ajoutant une STDOUT.flush
instruction à l'intérieur du bloc dans random.rb. Mais si vous ne pouvez pas changer la source, vous devez contourner ce problème. Vous ne pouvez pas le vider de l'extérieur du processus.
Si le sous-processus peut imprimer sur le shell en temps réel, il doit également y avoir un moyen de capturer cela avec Ruby en temps réel. Et voici. Vous devez utiliser le module PTY, inclus dans le noyau ruby je crois (1.8.6 de toute façon). Le plus triste, c'est que ce n'est pas documenté. Mais j'ai trouvé quelques exemples d'utilisation heureusement.
Premièrement, pour expliquer ce qu'est PTY, il signifie pseudo terminal . Fondamentalement, cela permet au script ruby de se présenter au sous-processus comme s'il s'agissait d'un utilisateur réel qui venait de taper la commande dans un shell. Ainsi, tout comportement modifié qui se produit uniquement lorsqu'un utilisateur a démarré le processus via un shell (tel que STDOUT n'étant pas mis en mémoire tampon, dans ce cas) se produira. Dissimuler le fait qu'un autre processus a démarré ce processus vous permet de collecter le STDOUT en temps réel, car il n'est pas mis en mémoire tampon.
Pour que cela fonctionne avec le script random.rb en tant qu'enfant, essayez le code suivant:
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