Reputation: 83
I have been searching for a way to run an interactive command (such as ssh
) and capture its output. This sounds simple but as I understand it, my use case is slightly different from a lot of the google and s/o results.
To be clear, by interactive I mean, the user who runs my_ruby_script.rb
from the terminal should be able to
stdin
) the running interactive system command (e.g., type ls
through ssh
tunnel)stdout
/stderr
) the running interactive system command (again e.g., seeing the output of ls
in an ssh
tunnel)On top of that, I would like to read the output of the system command (ssh
) so I may, for example, check for errors from inside my Ruby script. The closest that I have gotten has been to simply tee
the output to another file.
system('ssh username@ssh_server |& tee ssh_log.txt')
This is not ideal as
As I understand it, most of the solutions online (backticks, Open3.*
, PTY.spawn()
, etc. ) redirect stdin/stdout away from the terminal interface and into the ruby program. What I'm looking for is to leave stdin
alone, and tee stdout
to my Ruby script. Does anybody have any ideas?
I should note that system('ssh username@ssh_server')
does exactly what I want, I just need to be able to read the output. Additionally, I would prefer not to use net-ssh nor any other non-stdlib libraries.
I really appreciate any insight you can deliver.
Upvotes: 4
Views: 600
Reputation: 83
I found my answer! Big thanks to @Kimmo Lehto for suggesting the IO.copy_stream()
idea which led to my final solution. Basically, we use PTY.spawn(cmd){|stdin, stdout, pid| ...}
to get unbuffered streams for stdin
and stdout
.
Then we copy the script's stdin
to the command's stdin
with IO.copy_stream($stdin.raw!, $stdin)
(don't forget to set $stdin.cooked!
afterwards).
I couldn't figure out how to use copy_stream
on the command's stdout
twice (first to the script's stdout
, second to a buffer e.g., io = StringIO.new
) so I had to manually read each character like so: stdout.readchar(|c| print c; io.write c)
.
Upvotes: 1