# Highly inspired by https://github.com/sickill/ascii.io-cli/blob/master/bin/asciiio require 'pty' CTRL_C_CODE = ?\C-c def record(command) old_state = `stty -g` begin # stolen from ruby/ext/pty/script.rb # disable echoing and enable raw (not having to press enter) system "stty -echo raw lnext ^_" f = File.open("test.out", "w") f.sync = true writer = nil PTY.spawn(command) do |r_pty, w_pty, pid| reader = Thread.current writer = Thread.new do while true break if (ch = STDIN.getc).nil? ch = ch.chr if ch == CTRL_C_CODE reader.raise Interrupt, "Interrupted by user" else w_pty.print ch w_pty.flush end end end writer.abort_on_exception = true loop do c = begin r_pty.sysread(2048) rescue Errno::EIO, EOFError nil end break if c.nil? f.write c STDOUT.print c STDOUT.flush end begin # try to invoke waitpid() before the signal handler does it Process::waitpid2(pid) rescue Errno::ECHILD # the signal handler managed to call waitpid() first; # PTY::ChildExited will be delivered pretty soon, so just wait for it sleep 1 end end rescue PTY::ChildExited => e # Quit ensure writer && writer.kill system "stty #{ old_state }" end f.close end # record it record("/bin/zsh") # cleanup puts "\e[H\e[2J"