Как получить один символ без нажатия клавиши ввода?

Как я могу получить один символ клавиатуры из терминала с помощью Ruby без нажатия клавиши ввода? Я пробовал Curses::getch, но это не помогло мне.

Ответ 1

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.

Ответ 2

Так как ruby ​​2.0.0, в stdlib есть "io/console" с этой функцией

require 'io/console'
STDIN.getch

Ответ 3

@Jay дал отличный ответ, но есть две проблемы:

  • Вы можете испортить состояние по умолчанию tty;
  • Вы игнорируете управляющие символы (^ 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

Ответ 4

Примечание. Это и старый ответ, и решение больше не работает в большинстве систем.

Но ответ может быть полезен для некоторых сред, где другие методы не работают. Пожалуйста, прочитайте комментарии ниже.


Сначала вы должны установить highline:

gem install highline

Затем попробуйте, если метод highline работает для вас:

require "highline/system_extensions"
include HighLine::SystemExtensions

print "Press any key:"
k = get_character
puts k.chr

Ответ 5

Необработанный режим (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 находится только в сыром режиме на короткое время.