How can I get a single keyboard character from the terminal with Ruby without pressing enter?
I tried Curses::getch
, but that didn't really work for me.

- 158,662
- 42
- 215
- 303

- 5,261
- 4
- 22
- 15
-
2possible duplicate of [Get single char from console immediately](http://stackoverflow.com/questions/8072623/get-single-char-from-console-immediately) – Phrogz Nov 15 '11 at 21:05
6 Answers
Since ruby 2.0.0, there is a 'io/console' in the stdlib with this feature
require 'io/console'
STDIN.getch

- 1,743
- 1
- 13
- 16
-
Link to the documentation: http://ruby-doc.org/stdlib/libdoc/io/console/rdoc/IO.html#method-i-getch – Simon Perepelitsa May 12 '15 at 08:00
-
13This worked great, provided you check for control-C afterwards: `exit(1) if char == "\u0003"`. – Aidan Feldman Aug 01 '15 at 22:31
-
-
@philant nope, I have a script and calling it via `$ ruby script.rb` works. – Lucas Caton Sep 24 '17 at 22:22
-
This is probably the best answer nowadays because if functionality has to be changed then the ruby core team (e. g. nobu) can modify / adapt it. This should be better in the long term IMO. – shevy Feb 04 '21 at 08:10
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/2999
#!/usr/bin/ruby
begin
system("stty raw -echo")
str = STDIN.getc
ensure
system("stty -raw echo")
end
p str.chr
(Tested on my OS X system, may not be portable to all Ruby platforms). See http://www.rubyquiz.com/quiz5.html for some additional suggestions, including for Windows.

- 41,768
- 14
- 66
- 83
-
That works so far, but unfortunately i get a numeric and not a string. du you know how to convert the numeric to the right ascii-character? – Nino Oct 06 '08 at 16:33
-
2Yes, just use str.chr to get the character corresponding to the numeric value. I've updated the post to reflect that – Jay Oct 06 '08 at 16:50
-
1Unfortunately since you're in raw mode, control-C gets sent in as a character, not as a SIGINT. So if you want blocking input like above, but still want to let the user hit control-C to stop the program while it's waiting, make sure to do this: `Signal.trap("INT") { exit }` (see my answer below for a better formatted version) – AlexChaffee Nov 25 '11 at 21:10
-
2
@Jay gave a great answer, but there are two problems:
- You can mess up default tty state;
- You ignore control characters (^C for SIGINT, etc).
A simple fix for that is to save previous tty state and use following parameters:
-icanon
- disable canonical input (ERASE and KILL processing);isig
- enable the checking of characters against the special control characters INTR, QUIT, and SUSP.
In the end you would have a function like this:
def get_char
state = `stty -g`
`stty raw -echo -icanon isig`
STDIN.getc.chr
ensure
`stty #{state}`
end
Raw mode (stty raw -echo
) unfortunately causes control-C to get sent in as a character, not as a SIGINT. So if you want blocking input like above, but allow the user to hit control-C to stop the program while it's waiting, make sure to do this:
Signal.trap("INT") do # SIGINT = control-C
exit
end
And if you want non-blocking input -- that is, periodically check if the user has pressed a key, but in the meantime, go do other stuff -- then you can do this:
require 'io/wait'
def char_if_pressed
begin
system("stty raw -echo") # turn raw input on
c = nil
if $stdin.ready?
c = $stdin.getc
end
c.chr if c
ensure
system "stty -raw echo" # turn raw input off
end
end
while true
c = char_if_pressed
puts "[#{c}]" if c
sleep 1
puts "tick"
end
Note that you don't need a special SIGINT handler for the non-blocking version since the tty is only in raw mode for a brief moment.

- 8,092
- 2
- 49
- 55
-
This answer is great, and exactly what I was looking for. Thanks so much for exceptionally functional code! – eddieroger Nov 29 '12 at 19:29
-
1`Signal.trap('INT') { exit }` makes the program exit when control-C is pressed (unless it's bypassed by other means) and definitely works in 1.9.2-p290, so I don't know what "did not work for me" could mean – AlexChaffee Jan 19 '13 at 22:23
-
This works in your example, but it definitely does not work if you're using `STDIN.getch`. Then your SIGINT gets interpreted as `"\u0003"`. Or maybe something else depending on your terminal. – Justin Force Nov 18 '15 at 01:11
Note: This is and old answer and the solution no longer works on most systems.
But the answer could still be useful for some environments, where the other methods don't work. Please read the comments below.
First you have to install highline:
gem install highline
Then try if the highline method works for you:
require "highline/system_extensions"
include HighLine::SystemExtensions
print "Press any key:"
k = get_character
puts k.chr
-
5Warning: a recent commit removed this feature from highline. See https://github.com/JEG2/highline/issues/50 – AlexChaffee Jan 25 '13 at 18:53
-
2this does NOT work: it requires that you hit the
key. Incidently, it also takes in the \n character. – erapert May 08 '13 at 22:16 -
This [example](https://github.com/JEG2/highline/blob/master/examples/get_character.rb) works for me on Ruby 2.0 on Windows 7. – zhon Dec 18 '13 at 19:18
-
Works on Ruby 1.9.3 and Windows XP without need to press enter (tested on pry). ps Thanks! – Darek Nędza Jan 30 '14 at 18:37
And if you are building curses application, you need to call
nocbreak
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/curses/rdoc/Curses.html#method-c-cbreak

- 16,417
- 12
- 71
- 108