2010

pcめも、新年一発目。今年もよろしゅ。

ruby popen3 stdout+stderr

子プロセスの標準出力と標準エラー出力を両方読み取る話。 Ruby1.9 なら spawn を使うところだが、Ruby1.8 on unix だとどうすぺ。

はい、open3。単純化したのはこう。

#!/usr/bin/env ruby

require 'open3'

Open3.popen3("sh subproc.sh") do |i, o, e|
  i.close_write
  t1 = Thread.new {
    while line=o.gets do print line end
  }
  Thread.new {
    while line=e.gets do print line end
  }
  t1.join
  puts "Finished!"
end

Thread使えば実にシンプル。ところが、Ruby/tk と組み合わせると 意外に難儀する。Ruby/tk と RubyのThread が相性悪いのは よく言われていることだが、open3で上記のようなことを手軽に済ませたい場合は 困る。たとえば、子プロセスの出力をTkウィンドウに出したりしたいとき、 こんな風にするとうまく行かない。

#!/usr/bin/env ruby
require 'tk'
require 'open3'

text = TkText.new(:width=>80, :height=>10) {
  self.value = "[[ Output from Subprocess ]]\n"
}.pack
TkButton.new(:text => "DONE", :command => proc{exit}).pack

def update(textwig, string)
  textwig.value += string
  textwig.see('end')
  textwig.update
end

Open3.popen3("sh subproc.sh") do |i, o, e|
  i.close_write
  t1 = Thread.new {
    while line=o.gets do update(text, line) end
  }
  Thread.new {
    while line=e.gets do update(text, line) end
  }
  t1.join
  puts "Finished!"
end

Tk.mainloop

……って、あれうまく行かない例を作ったつもりが 動いちゃったよ。えーと、ま、いっか。サンプルとしてつこてくだされ。