Tôi đã thành công trong việc giải quyết vấn đề này của mình. Dưới đây là thông tin chi tiết, kèm theo một số giải thích, trong trường hợp bất kỳ ai gặp vấn đề tương tự tìm thấy trang này. Nhưng nếu bạn không quan tâm đến chi tiết, đây là câu trả lời ngắn gọn :
Sử dụng PTY.spawn theo cách sau (tất nhiên là với lệnh của riêng bạn):
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
Và đây là câu trả lời dài , với quá nhiều chi tiết:
Vấn đề thực sự dường như là nếu một quy trình không xóa rõ ràng stdout của nó, thì bất kỳ thứ gì được viết vào stdout sẽ được lưu vào bộ đệm thay vì thực sự được gửi, cho đến khi quá trình được thực hiện, để giảm thiểu IO (đây rõ ràng là một chi tiết triển khai của nhiều Thư viện C, được tạo ra để thông lượng được tối đa hóa thông qua IO ít thường xuyên hơn). Nếu bạn có thể dễ dàng sửa đổi quy trình để nó xả cặn thường xuyên, thì đó sẽ là giải pháp của bạn. Trong trường hợp của tôi, đó là máy xay sinh tố, vì vậy hơi đáng sợ đối với một noob hoàn chỉnh như tôi phải sửa đổi nguồn.
Nhưng khi bạn chạy các quy trình này từ shell, chúng hiển thị stdout sang shell trong thời gian thực và stdout dường như không được lưu vào bộ đệm. Tôi tin rằng nó chỉ được đệm khi được gọi từ một quy trình khác, nhưng nếu một shell đang được xử lý, thì stdout sẽ được nhìn thấy trong thời gian thực, không bị đệm.
Hành vi này thậm chí có thể được quan sát với một quy trình ruby vì quy trình con mà đầu ra của nó phải được thu thập trong thời gian thực. Chỉ cần tạo một tập lệnh, random.rb, với dòng sau:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
Sau đó, một tập lệnh ruby để gọi nó và trả về đầu ra của nó:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
Bạn sẽ thấy rằng bạn không nhận được kết quả trong thời gian thực như bạn có thể mong đợi, nhưng tất cả ngay sau đó. STDOUT đang được lưu vào bộ đệm, mặc dù nếu bạn tự chạy random.rb, nó không được lưu vào bộ đệm. Điều này có thể được giải quyết bằng cách thêm một STDOUT.flush
câu lệnh bên trong khối trong random.rb. Nhưng nếu bạn không thể thay đổi nguồn, bạn phải giải quyết vấn đề này. Bạn không thể xả nó từ bên ngoài quy trình.
Nếu quy trình con có thể in ra shell trong thời gian thực, thì cũng phải có cách để nắm bắt điều này với Ruby trong thời gian thực. Và có. Tôi tin rằng bạn phải sử dụng mô-đun PTY, có trong lõi ruby (1.8.6). Điều đáng buồn là nó không được ghi lại. Nhưng tôi may mắn tìm thấy một số ví dụ về việc sử dụng.
Đầu tiên, để giải thích PTY là gì, nó là viết tắt của pseudo terminal . Về cơ bản, nó cho phép tập lệnh ruby tự trình bày với quy trình con như thể người dùng thực sự vừa nhập lệnh vào một trình bao. Vì vậy, bất kỳ hành vi thay đổi nào chỉ xảy ra khi người dùng đã bắt đầu quá trình thông qua một trình bao (chẳng hạn như STDOUT không được lưu vào bộ đệm, trong trường hợp này) sẽ xảy ra. Việc che giấu sự thật rằng một quá trình khác đã bắt đầu quá trình này cho phép bạn thu thập STDOUT trong thời gian thực, vì nó không được lưu vào bộ đệm.
Để làm cho điều này hoạt động với tập lệnh random.rb ở dạng con, hãy thử mã sau:
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