【问题标题】:Hiding user input on terminal in Linux script在 Linux 脚本中隐藏终端上的用户输入
【发布时间】:2011-05-18 00:48:51
【问题描述】:

我有如下 bash 脚本:

#!/bin/bash

echo "Please enter your username";
read username;

echo "Please enter your password";
read password;

我希望当用户在终端上输入密码时,不应该显示它(或者应该显示像 ******* 之类的东西)。我如何做到这一点?

【问题讨论】:

  • 一般说明只是为了防止混淆:此用户名/密码与 linux 用户名/密码无关 - 我只是在寻找一种方法来隐藏用户在“读取密码”期间键入的数据.
  • 我添加了一个更新,如果你想在他们输入密码时输出* 来获得幻想
  • 非常感谢。如果有人知道一个问题 - 这会自动阻止它进入 .bash_history 吗?
  • 我不认为它会,bash_history 只捕获你的命令,运行你的命令后会发生什么它不会捕获。

标签: linux bash scripting


【解决方案1】:

@SiegeX 和 @mklement0 的出色贡献的一个变体:屏蔽用户输入;处理退格;但只有退格用于用户输入的长度(因此我们不会清除同一行上的其他字符)并处理控制字符等......经过这么多挖掘,找到了here这个解决方案!

#!/bin/bash
#
# Read and echo a password, echoing responsive 'stars' for input characters
# Also handles: backspaces, deleted and ^U (kill-line) control-chars
#
unset PWORD
PWORD=
echo -n 'password: ' 1>&2
while true; do
  IFS= read -r -N1 -s char
  # Note a NULL will return a empty string
  # Convert users key press to hexadecimal character code
  code=$(printf '%02x' "'$char") # EOL (empty char) -> 00
  case "$code" in
  ''|0a|0d) break ;;   # Exit EOF, Linefeed or Return
  08|7f)  # backspace or delete
      if [ -n "$PWORD" ]; then
        PWORD="$( echo "$PWORD" | sed 's/.$//' )"
        echo -n $'\b \b' 1>&2
      fi
      ;;
  15) # ^U or kill line
      echo -n "$PWORD" | sed 's/./\cH \cH/g' >&2
      PWORD=''
      ;;
  [01]?) ;;                        # Ignore ALL other control characters
  *)  PWORD="$PWORD$char"
      echo -n '*' 1>&2
      ;;
  esac
done
echo
echo $PWORD

【讨论】:

    【解决方案2】:

    对于不使用 bash 或 read 的某些功能的解决方案,您可以使用 stty 禁用回显

    stty_orig=$(stty -g)
    stty -echo
    read password
    stty $stty_orig
    

    【讨论】:

    • 这个解决方案对于一些裸“sh” shell 的实现非常有帮助,比如 Debian 的 dash shell。
    【解决方案3】:

    获取用户名和密码

    让阅读更清晰,但放在屏幕上方更好的位置

    #!/bin/bash
    clear
    echo 
    echo 
    echo
    counter=0
    unset username
    prompt="  Enter Username:"
    while IFS= read -p "$prompt" -r -s -n 1 char
    do
        if [[ $char == $'\0' ]]; then
            break
        elif [ $char == $'\x08' ] && [ $counter -gt 0 ]; then
            prompt=$'\b \b'
            username="${username%?}"
            counter=$((counter-1))
        elif [ $char == $'\x08' ] && [ $counter -lt 1 ]; then
            prompt=''
            continue
        else
            counter=$((counter+1))
            prompt="$char"
            username+="$char"
        fi
    done
    echo
    unset password
    prompt="  Enter Password:"
    while IFS= read -p "$prompt" -r -s -n 1 char
    do
        if [[ $char == $'\0' ]]; then
            break
        elif [ $char == $'\x08' ] && [ $counter -gt 0 ]; then
            prompt=$'\b \b'
            password="${password%?}"
            counter=$((counter-1))
        elif [ $char == $'\x08' ] && [ $counter -lt 1 ]; then
            echo
            prompt="  Enter Password:"
            continue
        else
            counter=$((counter+1))
            prompt='*'
            password+="$char"
        fi
    done
    

    【讨论】:

      【解决方案4】:

      与@lesmana 的回答有点不同(但大多类似)

      stty -echo
      read password
      stty echo
      

      简单地说:隐藏回声 做你的事 显示回声

      【讨论】:

      • 你能解释一下为什么这比@lesmana's answer更好吗?我发现另一个stty 调用的开销更小,堆栈上的变量也更少——只带走回声,只恢复回声。有什么可能的方式会扰乱其他stty 设置吗? Lesmana 保留所有设置。
      • 此答案假定在读取用户输入之前 echo 已打开(有时但通常不是安全的假设)。对于专业脚本,lesmana 更好。
      【解决方案5】:

      我总是喜欢使用 Ansi 转义字符:

      echo -e "Enter your password: \x1B[8m"
      echo -e "\x1B[0m"
      

      8m 使文本不可见,0m 将文本重置为“正常”。 -e 使 Ansi 逃脱成为可能。

      唯一需要注意的是你仍然可以复制和粘贴那里的文本,所以如果你真的想要安全,你可能不应该使用它。

      它只是让人们在您输入密码时不会看到您的密码。只是之后不要让您的计算机开机。 :)


      注意:

      只要支持 Ansi 转义序列,以上内容就与平台无关。

      但是,对于另一个 Unix 解决方案,您可以简单地告诉 read 不要回显字符...

      printf "password: "
      let pass $(read -s)
      printf "\nhey everyone, the password the user just entered is $pass\n"
      

      【讨论】:

        【解决方案6】:

        这是@SiegeX 出色的*-printing 解决方案的一个变体,用于bash支持退格;这允许用户使用backspace 键(Mac 上的delete 键)更正他们的输入,这通常由密码提示支持:

        #!/usr/bin/env bash
        
        password=''
        while IFS= read -r -s -n1 char; do
          [[ -z $char ]] && { printf '\n'; break; } # ENTER pressed; output \n and break.
          if [[ $char == $'\x7f' ]]; then # backspace was pressed
              # Remove last char from output variable.
              [[ -n $password ]] && password=${password%?}
              # Erase '*' to the left.
              printf '\b \b' 
          else
            # Add typed char to output variable.
            password+=$char
            # Print '*' in its stead.
            printf '*'
          fi
        done
        

        注意:


        POSIX-only shell 中(例如,sh 在 Debian 和 Ubuntu 上,shdash),使用 stty -echo 方法 (这是次优的,因为它打印 nothing),因为内置的 read 不支持 -s-n 选项。

        【讨论】:

        • 谢谢,@Dylan。我刚刚意识到从目前输入的密码中删除最后一个字符的代码可以简化 - 请参阅更新。
        【解决方案7】:

        这是@SiegeX 答案的变体,它适用于传统的 Bourne shell(不支持 += 分配)。

        password=''
        while IFS= read -r -s -n1 pass; do
          if [ -z "$pass" ]; then
             echo
             break
          else
             printf '*'
             password="$password$pass"
          fi
        done
        

        【讨论】:

        • 传统的 Bourne shell(或 POSIX 兼容的 shell)也无法识别 [[ ... ]],其内置的 read 也不会具有 -s-n 选项。例如,您的代码将无法在dash 中运行。 (它可以在sh 实际上是bash 的平台上工作,例如在OSX 上,bash 在以sh 调用时只是修改了一些默认选项,同时仍然支持大多数bashisms。)
        • 感谢您的反馈,已切换到单个 [,但如果您的 read 缺少这些选项,我显然无能为力。
        【解决方案8】:

        更新

        如果您想通过为他们键入的每个字符输出一个 * 来获得幻想,您可以执行以下操作(使用 andreas 的 read -s 解决方案):

        unset password;
        while IFS= read -r -s -n1 pass; do
          if [[ -z $pass ]]; then
             echo
             break
          else
             echo -n '*'
             password+=$pass
          fi
        done
        

        不花哨

        echo "Please enter your username";
        read username;
        
        echo "Please enter your password";
        stty -echo
        read password;
        stty echo
        

        【讨论】:

        • 应该是IFS=$'\n',这样您才能真正完成输入密码。否则很好;非常聪明。
        • 不需要设置IFS=$'\n',因为读取的默认分隔符是-d $'\n'。中断 nul 字符串的 if 语句(发生在换行符上)是允许他们完成密码的原因。
        • 在使用 bash 3.x 在 FreeBSD 上进行测试时,我发现情况并非如此。使用 3.1.17 在 Linux 上进行测试会产生结果。目前我还没有手边的 BSD 盒子,但我会在明天尝试确认这一点,只是为了我自己的好奇心。
        • @Sorpigal:很有趣,让我知道你发现了什么。我只关心便携性
        • 我明白了。我的 FreeBSD 测试基于从您对 lesmana 帖子的原始错误编辑中复制和粘贴的代码,其中包含一个重要区别:您通过了 read-d ''。当我稍后在 Linux 上重试时,我使用了重新发布的版本。如果我尝试省略 -d '' 当然它在 FreeBSD 上的工作原理是一样的!我其实很欣慰,这里没有什么神秘的平台魔法在起作用。
        【解决方案9】:

        只需将 -s 提供给您的 read 调用,如下所示:

        $ read -s PASSWORD
        $ echo $PASSWORD
        

        【讨论】:

        • 我更喜欢这种方式。如果我没有达到每日限额,我会投票给你
        • 提供上下文:-s 在输入时显示 nothing。 (-s 是一个非 POSIX 扩展,所以并不是所有的 shell 都支持它,比如 BusyBox 自带的ash shell;在这样的 shell 中使用ssty -echo 方法)
        • @electron man 页面中的任何内容实际上都表明-s 将文本颜色设置为与背景颜色相同。它只是“无声地回响”。 ss64.com/bash/read.htmlSilent mode. If input is coming from a terminal, characters are not echoed.
        • @electron 我在我的盒子上测试过,不仅它没有移动光标,当我尝试突出显示我输入密码的行时,我似乎无法稍后粘贴它。如果这本书说的是真的,两者都应该发生。我猜可能是另一种风格的 shell(我使用的是 bash 4.1.2(1))。
        • @DominykasMostauskis 是的,但标签是针对 bash,因此是解决方案。可能有大量的外壳变体不起作用:)
        猜你喜欢
        • 1970-01-01
        • 2021-01-27
        • 2017-04-16
        • 1970-01-01
        • 2014-06-25
        • 2017-06-12
        • 2017-06-18
        • 1970-01-01
        相关资源
        最近更新 更多