Как скрыть ввод пароля с терминала в скрипте ruby - PullRequest
73 голосов
/ 26 февраля 2010

Я новичок в рубине. Мне нужно получить пароль в качестве ввода через команду gets.

Как скрыть пароль, введенный в терминале, во время gets вызова

Ответы [ 6 ]

178 голосов
/ 01 августа 2012

Можно также использовать ядро ​​ruby.

$ ri IO.noecho

 (from ruby core)
 ------------------------------------------------------------------------------
   io.noecho {|io| }
  ------------------------------------------------------------------------------

 Yields self with disabling echo back.

   STDIN.noecho(&:gets)

 will read and return a line without echo back.

Для 1.9.3 (и выше) это требует добавления require 'io/console' к вашему коду.

require 'io/console'
text = STDIN.noecho(&:gets)
30 голосов
/ 26 февраля 2010

Существует библиотека с именем highline , которая работает следующим образом:

require 'rubygems'
require 'highline/import'

password = ask("Enter password: ") { |q| q.echo = false }
# do stuff with password
18 голосов
/ 26 февраля 2010

Лучший метод из ответа @ eclectic923:

require 'io/console'
password = STDIN.noecho(&:gets).chomp

Для 1.9.3 (и выше) это требует добавления require 'io/console' к вашему коду.

Оригинальный ответ:

Ruby " Password " - другая альтернатива.

16 голосов
/ 28 марта 2013

Как уже упоминалось, вы можете использовать IO#noecho для Ruby> = 1,9. Если вам нужна поддержка 1.8, вы можете использовать встроенную функцию оболочки read:

begin
  require 'io/console'
rescue LoadError
end

if STDIN.respond_to?(:noecho)
  def get_password(prompt="Password: ")
    print prompt
    STDIN.noecho(&:gets).chomp
  end
else
  def get_password(prompt="Password: ")
    `read -s -p "#{prompt}" password; echo $password`.chomp
  end
end

Теперь получить пароль так же просто, как:

@password = get_password("Enter your password here: ")

Примечание: В реализации, использующей read выше, у вас возникнут проблемы, если вы (или какой-либо другой клиент get_password) передадите специальные символы оболочки в приглашении (например, * 1018) * / " / ' / и т.д.). В идеале вы должны экранировать строку приглашения, прежде чем передавать ее в оболочку. К сожалению, Shellwords недоступен в Ruby 1.8. К счастью, вы можете сделать бэкап соответствующих битов самостоятельно (в частности, shellescape). С этим вы можете сделать это небольшое изменение:

  def get_password(prompt="Password: ")
    `read -s -p #{Shellwords.shellescape(prompt)} password; echo $password`.chomp
  end

Я упомянул пару проблем с использованием read -s -p в комментарии ниже:

Ну, кейс 1.8 немного дергается; это не позволяет обратная косая черта, если вы не нажмете обратную косую черту дважды: «Символ обратной косой черты `\ 'может использоваться для удаления любого специального значения для следующего символа читать и для продолжения строки. "Также:" Символы в значении переменная IFS используется для разделения строки на слова. «Это должно вероятно, подойдет для большинства маленьких сценариев, но вы, вероятно, захотите что-то более надежное для больших приложений.

Мы можем решить некоторые из этих проблем, засучив рукава и выполнив это с помощью stty(1). Краткое описание того, что нам нужно сделать:

  • Сохранить текущие настройки терминала
  • Поворот эха
  • Распечатайте приглашение и получите ввод пользователя
  • Восстановление настроек терминала

Мы также должны позаботиться о восстановлении настроек терминала, если они прерваны сигналами и / или исключениями. Следующий код будет правильно обрабатывать сигналы управления заданиями (SIGINT / SIGTSTP / SIGCONT), в то же время хорошо играя с любыми существующими обработчиками сигналов:

require 'shellwords'
def get_password(prompt="Password: ")
  new_sigint = new_sigtstp = new_sigcont = nil
  old_sigint = old_sigtstp = old_sigcont = nil

  # save the current terminal configuration
  term = `stty -g`.chomp
  # turn of character echo
  `stty -echo`

  new_sigint = Proc.new do
    `stty #{term.shellescape}`
    trap("SIGINT",  old_sigint)
    Process.kill("SIGINT", Process.pid)
  end

  new_sigtstp = Proc.new do
    `stty #{term.shellescape}`
    trap("SIGCONT", new_sigcont)
    trap("SIGTSTP", old_sigtstp)
    Process.kill("SIGTSTP", Process.pid)
  end

  new_sigcont = Proc.new do
    `stty -echo`
    trap("SIGCONT", old_sigcont)
    trap("SIGTSTP", new_sigtstp)
    Process.kill("SIGCONT", Process.pid)
  end

  # set all signal handlers
  old_sigint  = trap("SIGINT",  new_sigint)  || "DEFAULT"
  old_sigtstp = trap("SIGTSTP", new_sigtstp) || "DEFAULT"
  old_sigcont = trap("SIGCONT", new_sigcont) || "DEFAULT"

  print prompt
  password = STDIN.gets.chomp
  puts

  password
ensure
  # restore term and handlers
  `stty #{term.shellescape}`

  trap("SIGINT",  old_sigint)
  trap("SIGTSTP", old_sigtstp)
  trap("SIGCONT", old_sigcont)
end
9 голосов
/ 26 сентября 2016

Начиная с Ruby версии 2.3.0, вы можете использовать метод IO#getpass, например:

require 'io/console'
password = STDIN.getpass("Password:")

См. Метод getpass в документации стандартной библиотеки.

5 голосов
/ 06 августа 2014

Для ruby ​​версии 1.8 (или Ruby <1.9) я использовал <a href="http://ss64.com/bash/read.html" rel="noreferrer" title="read"> read встроенную оболочку, как упомянуто @ Charles.

Размещение кода, достаточного для запроса имени пользователя и пароля, где имя пользователя будет отображаться на экране при вводе, однако введенный пароль будет молчать.

 userid = `read -p "User Name: " uid; echo $uid`.chomp
 passwd = `read -s -p "Password: " password; echo $password`.chomp
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...