3

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?

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Ben D
  • 465
  • 1
  • 6
  • 20

2 Answers2

2

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
Casper
  • 33,403
  • 4
  • 84
  • 79
-1

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
egwspiti
  • 957
  • 5
  • 10