Reputation: 475
I'm writing a small script in Ruby that reads input from the command line.
I would like to catch the up , down , left, right arrow signal. I want to do something like in the Terminal. (when you hit up-arrow you have the previous command ...) How can I do that?
Upvotes: 3
Views: 2703
Reputation: 987
Starting with ruby 1.9.3, io/console
is shipped with ruby. One can use its #raw
to achieve unbuffered input:
http://ruby-doc.org/stdlib-2.2.2/libdoc/io/console/rdoc/IO.html#method-i-raw
However, arrow keys are represented by a sequence of characters, not a single character. This sequence always starts with "\e"
but unfortunately there isn't any end-of-sequence marker.
Something like this could be used to read arrow keys:
require 'io/console'
require 'timeout'
def readkey
c = ''
result = ''
$stdin.raw do |stdin|
c = stdin.getc
result << c
if c == "\e"
begin
while (c = Timeout::timeout(0.0001) { stdin.getc })
result << c
end
rescue Timeout::Error
# no action required
end
end
end
result
end
puts readkey.inspect #=> outputs "\e[D" if left arrow is pressed
Upvotes: 0
Reputation: 34308
To do completely unbuffered input you can use something like termios
. However you will have to interpret arrow key sequences manually.
If you can live with a middle layer for history completion I suggest using GNU readline, like mentioned previously, or the RawLine library by H3RALD:
http://www.h3rald.com/rawline/
http://www.h3rald.com/articles/real-world-rawline-usage/
Example of unbuffered input with termios
:
require 'rubygems'
require 'termios'
def with_unbuffered_input
old_attrs = Termios.tcgetattr(STDOUT)
new_attrs = old_attrs.dup
new_attrs.lflag &= ~Termios::ECHO
new_attrs.lflag &= ~Termios::ICANON
Termios::tcsetattr(STDOUT, Termios::TCSANOW, new_attrs)
yield
ensure
Termios::tcsetattr(STDOUT, Termios::TCSANOW, old_attrs)
end
with_unbuffered_input do
10.times {
c = STDIN.getc
puts "Got #{c}"
}
end
Upvotes: 2