ローファイ日記

出てくるコード片、ぼくが書いたものは断りがない場合 MIT License としています http://udzura.mit-license.org/

Rubyのptyライブラリで標準入力を渡す

すごく昔こういう記事を書いたんだけれど。

udzura.hatenablog.jp

フィヨルド方面より、まさかのこの記事に関する質問が来て、さらに「入力を渡すにはどうすればいいか」という追加質問がきたのであった。

試した感じ、なんとも素直に動かなかったので、どうすればいいかを書き残しておこうと思う。

ポイントは2つで、

  • ttyの入力を終了させるために、 ^D に相当する EOF EOT コードこと "\u{04}" を送り込む
  • 入力した行はそのまま pty_out から取ろうとするとフィードバックして取得できてしまうので、1行捨てる

これで大体想定した挙動になるはず。ただ、当然stdout/errが混ざるので、標準出力やエラー出力のテストなら Open3.capture3 などが適切だと思う。

サンプルコード

require 'pty'
status = nil
PTY.spawn("tac") do |pty_out, pty_in, pid|
  3.times do |i|
    # 書いたら捨てる
    pty_in.write "Hola" * (i+1) + "\n"
    pty_out.gets
  end

  # ^D 相当の制御文字
  pty_in.puts "\u{04}"
  pty_out.gets

  while !(status ||= PTY.check(pid)) && l = pty_out.gets
    puts "PTY(out): #{l}"
  end

  # MacOS では上のチェックでstatusが埋まる前に、
  # pty_out.gets でnilを返してループを抜けるので改めて取得する
  until status ||= PTY.check(pid)
  end
end

p status

結果

$ ruby pty.rb
PTY(out): HolaHolaHola
PTY(out): HolaHola
PTY(out): Hola
#<Process::Status: pid 2217 exit 0>