Open3で標準出力と標準エラー出力からの出力を全て読む
RubyにはOpen3というプロセスの起動とプロセス間通信用にIOポートの接続を行う便利なライブラリがある.
しかし,起動したプロセスの標準出力と標準エラー出力の両方を読もうとすると,デッドロックのような状態になってなかなか上手く行かない.
ググってみると,Threadを使うなどといった方法があるそうだが,とりあえず次のように書いてみた:
Open3.popen3(exe_name) {|stdin, stdout, stderr| stdin.puts(*input_to_stdin) stdin.close outid = stdout.fileno errid = stderr.fileno read_port = nil fileno_map_to_buff = { outid => external_out, errid => external_err, } fileno_map_port = { outid => stdout, errid => stderr, } while not fileno_map_port.empty? begin read_port_selected, = IO.select(fileno_map_port.values) read_port = read_port_selected[0] buff = read_port.readline fileno_map_to_buff[read_port.fileno] << buff rescue EOFError => e fileno_map_port.delete(read_port.fileno) end end }
概要は,selectで各ポートが読み込み可能になるまで待ち,readlineでバッファを読む.EOFError例外を捕捉してselectで待つ必要が無くなったポートを削除する.各ポートと書き込み先の識別はIO#filenoで行っている,という感じだろうか.
特に例外を捕えて処理するのは微妙なところ…
もっと良い方法はないのかなぁ…
IOについては後でまた調べよう.
それと,このコードは結構適当(例外に頼るところとか)なので,もっと良い書き方に改良したい.