pty とは、擬似端末(Pseudo tTY)を扱うライブラリ。
典型的には以下のように使う。
require 'pty' status = nil PTY.spawn("for i in $(seq 1 10); do echo progress $i; sleep 1; done") do |pty_out, pty_in, pid| pty_in.close while l = pty_out.gets puts "PTY: #{l}" end status = PTY.check pid end # PTY: progress 1 # PTY: progress 2 # PTY: progress 3 # ... # PTY: progress 10 #=> nil p status #=> #<Process::Status: pid 91190 exit 0>
Kernal#spawn と違って、ブロックを伴うことができ、出力一行ごとに対して何かする、といった処理に向いている。
Open3.capture2 なども似たような機能を提供するが、こちらはttyがアサインされない。
Open3.capture2 'ruby -e "p STDOUT.tty?"' #=> ["false\n", #<Process::Status: pid 90573 exit 0>]
Open3系では擬似端末をアサインするオプションがないようだ(Open3.pipeline などでttyに繋いだりすればできるかもだがややこしいと思う)。一部のツールはttyにつながっている時とそうでない時とでログのフォーマットを勝手に変えたりする(Goのツールで logrus 使ってるやつとか)ので、困ってしまうことがある。そこでPTYが便利、という話でした。
追記、ttyの扱いの違いについて
Kernal#spawn
spawn "tty" #=> 91755 # 少し遅れてttyが得られるが、これは今ログインしている端末のttyを受け継いでいることがわかる # /dev/ttys010
Open3.capture2
Open3.capture2("tty") => ["not a tty\n", #<Process::Status: pid 91669 exit 1>]
PTY.spawn
PTY.spawn("tty") {|i, o, pid| o.close; while(l = i.gets); puts l; end; p PTY.check pid} # /dev/ttys016 #=> #<Process::Status: pid 91708 exit 0> PTY.spawn("tty") {|i, o, pid| o.close; while(l = i.gets); puts l; end; p PTY.check pid} # /dev/ttys017 #=> #<Process::Status: pid 91709 exit 0> # ...というように、毎回新しく作っている
詳細はソースで(ただし、C含む)
-
-
- -
-
(2021/05/06 追記)
入力を渡したい場合の対応を書いています。