开发者

How do I echo stars (*) when reading password with `read`?

What do I need to do for code in Bash, if I want to echo *s in place of password characters (or even just hide the characters comp开发者_JAVA百科letely) when the user types something in using read?


As Mark Rushakoff pointed out, read -s will suppress the echoing of characters typed at the prompt. You can make use of that feature as part of this script to echo asterisks for each character typed:

#!/bin/bash
unset password
prompt="Enter Password:"
while IFS= read -p "$prompt" -r -s -n 1 char
do
    if [[ $char == $'\0' ]]
    then
        break
    fi
    prompt='*'
    password+="$char"
done
echo
echo "Done. Password=$password"


I really liked the answer that Wirone gave, but I didn't like that the backspacing would continue removing characters even back into the "Enter password: " prompt.

I also had some issues where pressing keys too rapidly would cause some of the characters to actually print on the screen... never a good thing when prompting for a password. =)

The following is my modified version of Wirone's answer which addresses these issues:

#!/bin/bash

unset PASSWORD
unset CHARCOUNT

echo -n "Enter password: "

stty -echo

CHARCOUNT=0
while IFS= read -p "$PROMPT" -r -s -n 1 CHAR
do
    # Enter - accept password
    if [[ $CHAR == $'\0' ]] ; then
        break
    fi
    # Backspace
    if [[ $CHAR == $'\177' ]] ; then
        if [ $CHARCOUNT -gt 0 ] ; then
            CHARCOUNT=$((CHARCOUNT-1))
            PROMPT=$'\b \b'
            PASSWORD="${PASSWORD%?}"
        else
            PROMPT=''
        fi
    else
        CHARCOUNT=$((CHARCOUNT+1))
        PROMPT='*'
        PASSWORD+="$CHAR"
    fi
done

stty echo

echo $PASSWORD


read -s should put it in silent mode:

-s     Silent mode.  If input is coming from a terminal, characters are not echoed.

See the read section in man bash.


I would like to add something to Dennis Williamson's solution:

#!/bin/bash

unset password
echo -n "Enter password: "
while IFS= read -p "$prompt" -r -s -n 1 char
do
    # Enter - accept password
    if [[ $char == $'\0' ]] ; then
        break
    fi
    # Backspace
    if [[ $char == $'\177' ]] ; then
        prompt=$'\b \b'
        password="${password%?}"
    else
        prompt='*'
        password+="$char"
    fi
done

In above example script handles backspace correctly.

Source


I don't know about stars, but stty -echo is your friend:

 #!/bin/sh 
 read -p "Username: " uname 
 stty -echo 
 read -p "Password: " passw; echo 
 stty echo

Source: http://www.peterbe.com/plog/passwords-with-bash


If you don't care about it being interactive, you can simply do

read -s pass
echo "$pass" | sed 's/./*/g'

This will show a * for each character of the entered password after enter is pressed.


stty -echo
read something
stty echo

will stop user input being echoed to the screen for that read. Depending on what you are doing with prompts, you may want to add an extra echo command to generate a newline after the read.


I just made this Bash-specific function based on Dennis Williamson's, Wirone's and Logan VanCuren's answers:

ask() {
  local 'args' 'char' 'charcount' 'prompt' 'reply' 'silent'

  # Basic arguments parsing
  while [[ "${1++}" ]]; do
    case "${1}" in
      ( '--silent' | '-s' )
        silent='yes'
        ;;
      ( '--' )
        args+=( "${@:2}" )
        break
        ;;
      ( * )
        args+=( "${1}" )
        ;;
    esac
    shift || break
  done

  if [[ "${silent}" == 'yes' ]]; then
    for prompt in "${args[@]}"; do
      charcount='0'
      prompt="${prompt}: "
      reply=''
      while IFS='' read -n '1' -p "${prompt}" -r -s 'char'; do
        case "${char}" in
          # Handles NULL
          ( $'\000' )
            break
            ;;
          # Handles BACKSPACE and DELETE
          ( $'\010' | $'\177' )
            if (( charcount > 0 )); then
              prompt=$'\b \b'
              reply="${reply%?}"
              (( charcount-- ))
            else
              prompt=''
            fi
            ;;
          ( * )
            prompt='*'
            reply+="${char}"
            (( charcount++ ))
            ;;
        esac
      done
      printf '\n' >&2
      printf '%s\n' "${reply}"
    done
  else
    for prompt in "${args[@]}"; do
      IFS='' read -p "${prompt}: " -r 'reply'
      printf '%s\n' "${reply}"
    done
  fi
}

It could be used like:

$ ask Username
Username: AzureDiamond
AzureDiamond

$ ask -s Password
Password: *******
hunter2

$ ask First Second Third
First: foo
foo
Second: bar
bar
Third: baz
baz


@nxnev's answer didn't quite work for me, at least on macOS. I simplified it a bit, and now it's flawless:

#!/bin/bash

ask() {
    charcount='0'
    prompt="${1}: "
    reply=''
    while IFS='' read -n '1' -p "${prompt}" -r -s 'char'
    do
        case "${char}" in
            # Handles NULL
            ( $'\000' )
            break
            ;;
            # Handles BACKSPACE and DELETE
            ( $'\010' | $'\177' )
            if (( charcount > 0 )); then
                prompt=$'\b \b'
                reply="${reply%?}"
                (( charcount-- ))
            else
                prompt=''
            fi
            ;;
            ( * )
            prompt='*'
            reply+="${char}"
            (( charcount++ ))
            ;;
        esac
    done
    printf '\n' >&2
    printf '%s\n' "${reply}"
}

pwd="$(ask Password)"

echo "Your password is $pwd"


#!/bin/bash
echo "------------------------------"

n=7
echo " Enter Password :"

for (( i=1;i<n;i++ ))
do
    stty -echo
    read -r -s -n 1 char
    stty echo

    echo -n "*"
    pass+="$char"

done

echo " "
echo " Your password : $pass "

echo ""
echo "-------------------------------"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜