6

We can use the command stty -echo with a bash shell script to prevent characters to be echoed to the console, e.g., for password input.

Is there a way to print a * for each input character instead, and to delete them when pressing backspace?

2
  • Note that not echoing for each character is a deliberate security choice, as it reduces the likelihood that someone looking over the shoulder or in logs can determine the exact password length; knowing that makes brute-forcing and other cracking attempts relatively easier. So it may be perilous to override that decision. Commented Jul 24, 2024 at 4:52
  • 2
    @Miral I'd argue that the security benefit of that is completely negligible, but the reduced user experience of not getting any feedback is detrimental to security. Users who are frustrated when entering a password are more likely to choose passwords that are easier to enter (i.e. shorter). Not providing any feedback that characters are typed is terrible UX, which is why no other system does that. And honestly, since those terminal APIs date back about 5 decades to original Unix, I suspect not echoing anything is simply a historical artifact of being easy to implement. Commented Jul 24, 2024 at 7:52

3 Answers 3

5

Well, here it is in bash:

#!/bin/bash prompt="Enter password: " password="" # Turn off echo stty -echo # Print prompt printf "$prompt" while IFS= read -r -s -n1 char; do if [[ $char == $'\0' ]]; then break elif [[ $char == $'\177' ]]; then if [ ${#password} -gt 0 ]; then password="${password%?}" printf "\b \b" fi else password+="$char" printf "*" fi done # Turn echo back on stty echo printf "\n" # The password is now stored in the $password variable echo "Password entered: $password" 

This code will prompt the user for a password, display * for each character typed, and handle backspaces correctly. The entered password is stored in the password variable.

Some programming languages have that functionality already built-in.

2
  • 1
    Ok, this is a hack, but it works. I thought it could be something built in the shells to do this. Thanks! Commented Jul 23, 2024 at 12:22
  • 1
    I don't think there's any point in using stty -echo if you're also using -s with read, is there? Commented Jul 23, 2024 at 12:39
3

It maybe a bit overkill, but if you have GPG's pinentry programs installed, you can use those to present a nice interface to the user for password prompts, including asterisks for masking characters (enabled by default), password strength meters, password confirmation, etc. However, it uses a command-response system for communication over a pipe, instead of being simply invokable using options directly. So you send commands over pinentry's input to customise the prompt, actually prompt for the password, etc., and then parse the output (which will include status messages and such) to get the password.

Something like:

get_password() { # Get locale information https://unix.stackexchange.com/a/757655/70524 eval "$(locale)" # Use `$TTY` if available (zsh), `$(tty)` otherwise (bash) TTY=${TTY:-"$(tty)"} { echo SETPROMPT "Password:" # Set the prompt # Add more such customization commands here echo SETDESC "Enter password for $USER" # Set the description # Then finally ask for the password itself. echo GETPIN } | # pinentry assumes the frontend will be invoked in the background, # and needs to be explicitly told what environment it should use pinentry-curses --ttytype "$TERM" --ttyname "$TTY" --lc-ctype "$LC_CTYPE" --lc-messages "$LC_MESSAGES" | while IFS= read -r line; do case $line in OK*) continue;; D*) pass=${line#D }; printf "%s\n" "$pass";; # `D`ata line will have the password *) printf "%s\n" "$line" >&2;; # Dump everything else as possible errors esac; done } 

And use it like so:

password=$(get_password) 

This will open a full-screen dialog in the terminal:

 ┌────────────────────────────────────────────────────┐ │ Enter password for muru │ │ │ │ Password: ________________________________________ │ │ │ │ <OK> <Cancel> │ └────────────────────────────────────────────────────┘ 

(It's a perfect box in my terminal, but might not appear so on the webpage.)

You can also use pinentry-gnome, pinentry-qt, pinentry-mac, etc. for making GUI prompts if the appropriate environment is present.

4
  • Interesting idea. But I tried to run your command and got an error in the line (D *). Replaced that with D*), and got this error, bot in konsole and rxvt: S ERROR curses.open_tty_for_read 83918929 ERR 83918929 No such file or directory <Pinentry> Commented Jul 24, 2024 at 1:09
  • I tested this using zsh :facepalm: where $TTY is set, unlike in bash. Try with "$(tty)" instead of "$TTY". (Using zsh is also the reason for the case error, I think). Commented Jul 24, 2024 at 1:51
  • Hmm, not even plain tty is sufficient, because both stdin and stdout are redirected. We'll need to use $(tty <&2) instead, or get the TTY before the pipe. I've updated the post to set $TTY if unset using tty. Commented Jul 24, 2024 at 1:59
  • Neat! It works now. Thanks! Commented Jul 24, 2024 at 2:35
2

if you're using bash, you can just use the -s option of the read which suppresses input:

$ help read | grep -- -s -s do not echo input coming from a terminal 

You still need to hack showing the asterisks, but you can do this using the approach described by Dennis Williamson in this SO answer:

#!/bin/bash unset password prompt="Enter your password: " while IFS= read -rsp "$prompt" -n1 char do if [[ $char == $'\0' ]] then echo "" break fi prompt='*' password+="$char" done printf 'You entered: %s\n' "$password" 

However, this doesn't support deleting ay characters as the backspace is also just echoed as one more * instead of deleting. So Max Haase's answer, which does handle this, is better.

4
  • This does not work since backspace does not delete neither the entered character, nor the echoed wildcard. Commented Jul 23, 2024 at 13:55
  • @LuisA.Florit ah, no it won't. But that's the kind of thing that should be mentioned as a requirement. Max's answer does handle this nicely though. Commented Jul 23, 2024 at 13:57
  • I explicitly mentioned it: "and to delete them when pressing backspace". Sorry if it wasn't clear enough. Commented Jul 23, 2024 at 14:07
  • 1
    Sigh. No, @LuisA.Florit, that was absolutely clear enough. It isn't your fault that I didn't read carefully. Very much my bad, sorry! Commented Jul 23, 2024 at 14:47

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.