如何在Ruby脚本中隐藏从终端输入的密码


Answers:


21

来自@ eclectic923答案的最佳方法:

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

对于1.9.3(及更高版本),这需要您添加require 'io/console'代码。

原始答案:

Ruby“密码”是另一种选择。


谢谢..正是我在寻找
jsampath

10
不建议使用此答案,而应使用@ eclectic923核心红宝石解决方案。
notapatch

183

一个人也可以使用核心红宝石。

$ 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)

21
对于1.9.3,这需要您添加require 'io/console'代码。通常比任何宝石选项都要好。
icco 2012年

1
我无法对此答案+1足够。节拍每天都必须包括额外的宝石!
mydoghasworms

19
不要忘记.chomp您的输入!通过= STDIN.noecho(&:gets).chomp
avguchenko 2014年

1
您如何在Cygwin中使用它?tmp.rb:2:in 'noecho': Bad file descriptor (Errno::EBADF) from tmp.rb:2:in <main>
Chloe

30

有一个名为highline的库,其工作原理如下:

require 'rubygems'
require 'highline/import'

password = ask("Enter password: ") { |q| q.echo = false }
# do stuff with password

16

正如其他人提到的,您可以将IO#noechoRuby> = 1.9用于。如果您希望支持1.8,则可以使用read内置的shell函数:

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)在提示中传递特殊的shell字符(例如$/ "/ '/ etc),则会遇到麻烦。理想情况下,应在将提示字符串传递到外壳程序之前对其进行转义。不幸的是,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

好吧,1.8的情况有点儿陈旧;它不允许使用反斜杠,除非您两次按下反斜杠:“反斜杠字符'\'可以用于删除下一个读取的字符以及换行的任何特殊含义。” 另外:“ IFS变量值中的字符用于将行分割成单词。”对于大多数小脚本来说,这可能没问题,但对于大型应用程序,可能需要更强大的功能。
2013年


5

对于红宝石版本1.8(或Ruby <1.9),我使用了@Charles提到的内置读取shell。

只需输入足以提示用户名和密码的代码即可,在键入用户名时会在屏幕上回显用户名,但键入的密码将保持静音。

 userid = `read -p "User Name: " uid; echo $uid`.chomp
 passwd = `read -s -p "Password: " password; echo $password`.chomp
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.