How to get a single character without pressing ent

2019-01-04 09:54发布

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.

标签: ruby getch
6条回答
姐就是有狂的资本
2楼-- · 2019-01-04 10:33

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.

查看更多
混吃等死
3楼-- · 2019-01-04 10:34

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
查看更多
我欲成王,谁敢阻挡
4楼-- · 2019-01-04 10:34

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

查看更多
祖国的老花朵
5楼-- · 2019-01-04 10:45

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.

查看更多
男人必须洒脱
6楼-- · 2019-01-04 10:45

@Jay gave a great answer, but there are two problems:

  1. You can mess up default tty state;
  2. 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
查看更多
forever°为你锁心
7楼-- · 2019-01-04 10:56

Since ruby 2.0.0, there is a 'io/console' in the stdlib with this feature

require 'io/console'
STDIN.getch
查看更多
登录 后发表回答