44

如何在不按 Enter 的情况下使用 Ruby 从终端获取单个键盘字符?我试过Curses::getch了,但这对我来说真的不起作用。

4

6 回答 6

68

从 ruby​​ 2.0.0 开始,stdlib 中有一个具有此功能的“io/console”

require 'io/console'
STDIN.getch
于 2014-11-19T16:23:17.430 回答
37

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

(在我的 OS X 系统上测试,可能无法移植到所有 Ruby 平台)。请参阅http://www.rubyquiz.com/quiz5.html了解一些其他建议,包括针对 Windows 的建议。

于 2008-10-06T16:14:35.223 回答
21

@Jay 给出了一个很好的答案,但是有两个问题:

  1. 你可以搞乱默认的 tty 状态;
  2. 您忽略控制字符(^C 表示 SIGINT 等)。

一个简单的解决方法是保存以前的 tty 状态并使用以下参数:

  • -icanon- 禁用规范输入(ERASE 和 KILL 处理);
  • isig- 启用对特殊控制字符 INTR、QUIT 和 SUSP 的字符检查。

最后,您将拥有这样的功能:

def get_char
  state = `stty -g`
  `stty raw -echo -icanon isig`

  STDIN.getc.chr
ensure
  `stty #{state}`
end
于 2013-01-25T17:49:12.170 回答
14

不幸的是,原始模式 ( stty raw -echo) 导致 control-C 作为字符而不是 SIGINT 发送。因此,如果您想像上面那样阻塞输入,但允许用户在等待时按 control-C 来停止程序,请确保这样做:

Signal.trap("INT") do # SIGINT = control-C
  exit
end

如果你想要非阻塞输入——也就是说,定期检查用户是否按下了一个键,但与此同时,去做其他事情——那么你可以这样做:

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

请注意,非阻塞版本不需要特殊的 SIGINT 处理程序,因为 tty 仅在短时间内处于原始模式。

于 2011-11-25T21:13:36.517 回答
13

注意:这是旧答案,该解决方案不再适用于大多数系统。

但是对于其他方法不起作用的某些环境,答案仍然可能有用。请阅读下面的评论。


首先你必须安装highline:

gem install highline

然后尝试 highline 方法是否适合您:

require "highline/system_extensions"
include HighLine::SystemExtensions

print "Press any key:"
k = get_character
puts k.chr
于 2010-10-21T01:51:51.917 回答
0

如果你正在构建curses应用程序,你需要调用

nocbreak

http://www.ruby-doc.org/stdlib-1.9.3/libdoc/curses/rdoc/Curses.html#method-c-cbreak

于 2012-11-30T21:43:09.570 回答