I'm writing a simple REPL (a command line wrapper for adb
) in Ruby where I support two kinds of commands:
- interactive commands
- non-interactive commands
As for 2, I simply want to invoke a system command from the REPL, capture its output while it's outputting text, and allow the user to exit back into the REPL from that command. Example:
>> logcat
... // log output here
! user hits CTRL-D
>> // back at the prompt
This is to happen within my program, not the system shell.
Now the problem is: while logcat
is running, the parent process (the REPL) keeps capturing keystrokes and then replays (?) them as soon as the command exits. That means, if for instance hit return a few times, then CTRL-C, it will exit out of the sub command back into the REPL but replay all the keystrokes again, which the REPL doesn't understand (if one of those keystrokes happened to be CTRL-D, it will even unintentionally exit my program... this is obviously not what I want.)
Now I have tried multiple different ways of executing the sub command (via backtick, system, exec), I tried to flush
and rewind
$stdin
before entering back into the loop, I tried trapping the CTRL-C interrupt, but nothing works.
I'm sure there must be a way in Ruby to:
- start a sub process
- route all user input to that process and only that process (i.e. not the parent process)
- return to parent process on CTRL-C
Any idea how to accomplish that?
You can try something like the following:
Signal.trap("INT") {} # ignore sigint in the parent
IO.popen "sort" do |io| # run sort in a sub process
puts io.read # output stdout
end
Signal.trap("INT") { exit } # restore sigint
while true do
puts "looping"
sleep 1
end
If you run the program you can type in:
$ ruby test.rb
c
d
b
^D
b
c
d
looping
looping
^C
$
or
$ ruby test.rb
c
d
b
^C
looping
looping
^C
$
It works because popen runs the command in a sub process, which has its own signal handling.
来源:https://stackoverflow.com/questions/22426540/swallowing-user-input-while-running-a-sub-command