Bash:屏蔽用户输入的密码,支持退格和特殊字符

Bash: Masking user input for a password, with * support for backspace and special characters

我根据 @SiegeX and @mklement0 from this question.

屏蔽密码输入的想法在下面制作了这个片段

太棒了,我唯一想要的添加是只删除输入字符的长度,所以我们不会删除整行。

我不是很了解这个,所以 运行 遇到了错误。

在下面,输入“12345”并退格,数字不会“退格”;没有错误。

输入“123FourFive”并退格,产生错误:line 9: [[: 123FourFiv: value too great for base (error token is "123FourFiv")

输入“OneTwo345”并退格,似乎工作正常。

输入密码中可能期望的符号然后退格会产生错误:line 9: [[: OneTwo./?: syntax error: invalid arithmetic operator (error token is "./?")

在输入过程中按箭头键也会在退格后产生乱码...

如何改进这一点,以便我们屏蔽用户输入,只删除已输入的内容?

还是我们在“重新发明轮子”?还有其他东西可以做我已经尝试做的事情吗(这是屏蔽用户输入以在 bash 脚本中获取密码以将其放入变量中)?

用户环境是 Linux Mint 19.3 with Cinnamon。

#!/bin/bash

printf "\n\tPlease enter password: "
    # mask the input for the password by @SiegeX and @mklement0 (
    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
            # @nooblag, only delete for length of entered chars?
            if [[ "${password}" -lt "${#password}" ]]; then printf '\b \b'; fi # erase one '*' to the left.
            [[ -n $password ]] && password=${password%?} # remove last char from output variable
        else
            # add typed char to output variable
            password+="${char}"
            # print '*' in its stead
            printf '*'
        fi
    done

printf "\tPassword: ${password}\n\n"

更新: askpass 按照建议 here 几乎可以满足我的要求,但是如果用户尝试 abort/kill Ctrl+C 它弄乱了终端...

这可能是解决方案!摘自 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