(Ruby) PTY.spawn("bash -i")でコマンド実行してプロンプトをexpectしつつ途中の出力も随時表示する

  • expectで素朴にプロンプトを待つだけだとコマンドの実行が完了するまで途中の出力が表示できなくていまいちだったのであれこれ試して下記のようにしてみた。
  • プロンプトのパターンに改行が含まれる場合はもうちょい工夫(途中の出力をバッファリングしておく)が必要。
# coding: utf-8

require 'pty'
require 'expect'
require 'timeout'

puts "-->> spawn"

PTY.spawn("bash -i") do |i, o|
  # 最初の出力を出し切る
  begin
    Timeout.timeout(1) do
      loop { print i.getc }
    end
  rescue Timeout::Error
  end

  # 実行するコマンドを送信
  o.puts "ruby command.rb"

  finished = false

  while ! finished
    # プロンプトに加えて途中の出力にもマッチさせる
    i.expect(/(.*)\$ |(.*)\n/) do |m|
      print m[0]
      if m[1]
        # プロンプトが来た場合
        finished = true
      else
        # 途中の出力
      end
    end
  end
end

puts "<<-- spawn"

テスト用の command.rb

(1..3).each do |i|
  $stdout.puts "out #{i}"
  $stderr.puts "err #{i}"
  sleep 1
end
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]


追記 2018-02-25

単に標準出力に出したいだけなら $expect_verbose = true する方法もあります。

IO#expect のソースはコメントを除くと40行くらいなのでさらっと見ておくとよいです。場合によっては expect 自体をモンキーパッチするのもいいかも。
ruby/expect.rb at v2_5_0 · ruby/ruby