158

I have bash script like the following:

#!/bin/bash

echo "Please enter your username";
read username;

echo "Please enter your password";
read password;

I want that when the user types the password on the terminal, it should not be displayed (or something like *******) should be displayed). How do I achieve this?

jww
  • 97,681
  • 90
  • 411
  • 885
  • 2
    General note just to prevent confusion: this username/password has got nothing to do with the linux username/password - I am just looking for a way to hide the data that user types during "read password". –  Nov 30 '10 at 17:46
  • Thanks much. One question if someone knows - will this automatically prevent it from going into .bash_history? –  Nov 30 '10 at 19:09
  • 2
    possible duplicate of [How to get a password from a shell script without echoing](http://stackoverflow.com/questions/3980668/how-to-get-a-password-from-a-shell-script-without-echoing) and [How to make bash script ask for a password ?](http://stackoverflow.com/questions/2654009/how-to-make-bash-script-ask-for-a-password) and [How do i put stars into `read`?](http://stackoverflow.com/questions/1923435/how-do-i-put-stars-into-read) and others. – Dennis Williamson Nov 30 '10 at 20:55
  • 2
    I don't think it will, bash_history only captures your command, what happens after running your command it doesn't capture. – Andreas Wong Dec 01 '10 at 01:11
  • http://ss64.com/bash/read.html – dylnmc Sep 21 '14 at 04:11

9 Answers9

345

Just supply -s to your read call like so:

$ read -s PASSWORD
$ echo $PASSWORD
Andreas Wong
  • 59,630
  • 19
  • 106
  • 123
  • 5
    To provide context: `-s` displays _nothing_ while the input is typed. (`-s` is a non-POSIX extension, so not all shells support it, such as the `ash` shell that comes with BusyBox; use the `ssty -echo` approach in such shells) – mklement0 Apr 08 '14 at 13:30
  • Best solution. This - I have noticed - is what almost all password blocks use in Linux and Mac. – dylnmc Sep 21 '14 at 03:35
  • By using -s option ,read command sets the text color to the same as the background color. So why cant I copy the text I had entered? – Prateek Joshi Oct 06 '15 at 15:13
  • 4
    @electron Nothing in the `man` page actually suggests that `-s` sets the text color to the same as the the background color. It just "echoes silently". http://ss64.com/bash/read.html `Silent mode. If input is coming from a terminal, characters are not echoed.` – Andreas Wong Oct 07 '15 at 03:45
  • @SiGanteng Actually I was reading the book "Linux command line & shell scripting bible" and it is written on page 391 that "The -s option prevents the data entered in the read command from being displayed on the monitor; actually, the data is displayed, but the read command sets the text color to the same as the background color." . But I didn't found anything like this mentioned in the web. – Prateek Joshi Oct 07 '15 at 14:56
  • 2
    @electron I tested on my box, not only it doesn't move the cursor, when I try to highlight the line where I typed in my password, I can't seem to paste it later. Both should happen if what the book says is true. I'm guessing maybe a different flavor of shell (I was using bash 4.1.2(1)). – Andreas Wong Oct 08 '15 at 02:46
  • 1
    @DominykasMostauskis yeah but the label is for bash, therefore the solution. There are probably tons of shell variants where this don't work :) – Andreas Wong Jul 03 '18 at 10:27
  • I get an error with this approach `read: Illegal option -s` – Mark E Feb 02 '22 at 17:52
31

Update

In case you want to get fancy by outputting an * for each character they type, you can do something like this (using andreas' read -s solution):

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

Without being fancy

echo "Please enter your username";
read username;

echo "Please enter your password";
stty -echo
read password;
stty echo
Community
  • 1
  • 1
SiegeX
  • 135,741
  • 24
  • 144
  • 154
  • It should be `IFS=$'\n'` so that you can actually *finish* typing the password. Otherwise nice; very clever. – sorpigal Nov 30 '10 at 18:21
  • 2
    No need to set `IFS=$'\n'` because read's default delimiter is `-d $'\n'`. The if-statement to break on a nul string, which happens on a newline, is what allows them to finish the password. – SiegeX Nov 30 '10 at 18:25
  • In testing on FreeBSD with bash 3.x I find this not to be the case. Testing on Linux with 3.1.17 yields your results. I haven't got a BSD box handy at the moment but I will try to confirm this tomorrow, just for my own curiosity's sake. – sorpigal Nov 30 '10 at 21:14
  • @Sorpigal: interesting, let me know what you find out. I'm all about portability – SiegeX Nov 30 '10 at 23:39
  • Confirmed. On FreeBSD 7 with bash 3.2.48(1)-release (i386-portbld-freebsd7.0) your version does not terminate on enter. Setting IFS as I described does cause it to do so. On a Debian box with bash 3.1.17 it works as you describe. Could it be a difference in the defaults for `read` between bash 3.1 and 3.2? – sorpigal Dec 01 '10 at 11:07
  • @Sorpigal: I'm going to guess that's probably the case. What about this line of code, does this work on FreeBSD 7? `while IFS= read -r -d '' -s -n1 pass; do [[ $pass = $'\n' ]] && echo "you hit enter" && break; done` – SiegeX Dec 01 '10 at 16:41
  • With that line I am prompted for input and all normal key presses are ignored but enter, which aborts the loop. – sorpigal Dec 01 '10 at 17:05
  • 2
    I've got it. My FreeBSD test was based on the code copied and pasted from your original mistaken edit of lesmana's post, which contains one important difference: you had been passing `read` a `-d ''`. When I retried it later on Linux I used the reposted version. If I try omitting `-d ''` of course it works identically on FreeBSD! I am actually quite relieved that there isn't some mysterious platform magic at work here. – sorpigal Dec 01 '10 at 17:14
  • If you are worried about someone seeing the password, then do really want to let them see the length of it? – Craig Hicks Aug 28 '17 at 02:16
  • Security is in layers, this is yet just another. You could say the same about any online banking or shopping site. I'm sure people wouldn't be OK with those sites showing the plain text. – SiegeX Aug 28 '17 at 02:44
  • Of course you're right, I was being pedantic. It's easier just to point the hidden camera at the keyboard. – Craig Hicks Aug 31 '17 at 06:55
  • As a side point to this, you need an `stty -echo` at the beginning for the fancy version. Without that command rapid typing will leak characters onto the terminal (try holding a key for example). Using the `stty -echo` will still print the characters out, but will hide any input that didn't make it to the `read` command. – Elven Spellmaker May 17 '18 at 13:11
28

you can use stty to disable echo

this solution works without bash or certain features from read

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

If you use this in a shell script then also set an exit handler which restores echo:

#! /bin/sh
stty_orig=$(stty -g)
trap "stty ${stty_orig}" EXIT
stty -echo
...

this is to make sure echo is restored regardless of how the script exits. otherwise the echo will stay off if the script errors out.

to turn echo back on manually type the following command

stty echo

you will have to type blindly because you do not see what you type.

i suggest to press ctrl+c first to clear anything else you might have typed before.


trivia

echo means to echo your typed input back to your screen.

this is from the time we worked on teletypewriters (that is what the tty means). a teletypewriter is like a typewriter but connected to another teletypewriter or computer. typically via telephone cable.

the workflow on a teletypewriter is roughly as follows: you type in your command (or message for the other side). then the teletypewriter will print the response from the other side.

when you work on a teletypewriter you see your input as you type. this is because the teletypewriter is also a typewriter and as such prints the characters as you press them.

when teletypewriters where replaced by screens there was no longer a typewriter which types your input. instead we had to deliberate code an "echo" function which prints your input as you type.

i do not know whether stty -echo also disabled printing on a teletypewriter.

see here for a teletypewriter in action: https://www.youtube.com/watch?v=2XLZ4Z8LpEE (first part is restoration. action starting at about 12 minutes in)

more teletypewriter restoration: https://www.youtube.com/playlist?list=PL-_93BVApb5-9eQLTCk9xx16RAGEYHH1q

Lesmana
  • 25,663
  • 9
  • 82
  • 87
  • This solution is very helpful for some implementations of the bare "sh" shell, such as Debian's dash shell. – SunSplat Sep 28 '21 at 17:18
17

Here's a variation on @SiegeX's excellent *-printing solution for bash with support for backspace added; this allows the user to correct their entry with the backspace key (delete key on a Mac), as is typically supported by password prompts:

#!/usr/bin/env bash

password=''
while IFS= read -r -s -n1 char; do
  [[ -z $char ]] && { printf '\n' >/dev/tty; 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' >/dev/tty
  else
    # Add typed char to output variable.
    password+=$char
    # Print '*' in its stead.
    printf '*' >/dev/tty
  fi
done

Note:

  • As for why pressing backspace records character code 0x7f: "In modern systems, the backspace key is often mapped to the delete character (0x7f in ASCII or Unicode)" https://en.wikipedia.org/wiki/Backspace
  • \b \b is needed to give the appearance of deleting the character to the left; just using \b moves the cursor to the left, but leaves the character intact (nondestructive backspace). By printing a space and moving back again, the character appears to have been erased (thanks, The "backspace" escape character '\b': unexpected behavior?).

In a POSIX-only shell (e.g., sh on Debian and Ubuntu, where sh is dash), use the stty -echo approach (which is suboptimal, because it prints nothing), because the read builtin will not support the -s and -n options.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • @cave-at-aperture, the code already contains a check for whether the password is the empty string (`[ -n $password]`), and the `printf '\b \b'` is benign if it is. – mklement0 Jan 04 '23 at 18:49
  • @cave, I have improved the answer, however, by making the `printf` calls that serve the interactive input only target `/dev/tty`. – mklement0 Jan 04 '23 at 19:53
8

A bit different from (but mostly like) @lesmana's answer

stty -echo
read password
stty echo

simply: hide echo do your stuff show echo

yerlilbilgin
  • 3,041
  • 2
  • 26
  • 21
  • Can you explain why this is better than [@lesmana's answer](http://stackoverflow.com/a/4316765/4233593)? I see it's simpler with less overhead for another `stty` call, and one less variable on the stack - only taking away echo and only restoring echo. Is there any possible way this could upset other `stty` settings? Lesmana's preserves all settings. – Jeff Puckett May 30 '16 at 19:21
  • 1
    This answer presumes that echo was on prior to reading the user input (sometimes but not usually a safe assumption). For professional scripts, lesmana's is better. – SunSplat Sep 28 '21 at 17:29
4

I always like to use Ansi escape characters:

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

8m makes text invisible and 0m resets text to "normal." The -e makes Ansi escapes possible.

The only caveat is that you can still copy and paste the text that is there, so you probably shouldn't use this if you really want security.

It just lets people not look at your passwords when you type them in. Just don't leave your computer on afterwards. :)


NOTE:

The above is platform independent as long as it supports Ansi escape sequences.

However, for another Unix solution, you could simply tell read to not echo the characters...

printf "password: "
let pass $(read -s)
printf "\nhey everyone, the password the user just entered is $pass\n"
dylnmc
  • 3,810
  • 4
  • 26
  • 42
2

Here is a variation of @SiegeX's answer which works with traditional Bourne shell (which has no support for += assignments).

password=''
while IFS= read -r -s -n1 pass; do
  if [ -z "$pass" ]; then
     echo
     break
  else
     printf '*'
     password="$password$pass"
  fi
done
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • A traditional Bourne shell (or a POSIX-compliant one) would also not recognize `[[ ... ]]`, nor would its `read` builtin have the `-s` and `-n` options. Your code will therefore not work in `dash`, for instance. (It will work on platforms where `sh` is actually `bash`, such as on OSX, where `bash` when invoked as `sh` merely modifies a few default options while still supporting most bashisms.) – mklement0 Apr 08 '14 at 12:48
  • 2
    Thanks for feedback, switched to single `[` but obviously not much I can do if your `read` lacks these options. – tripleee Apr 08 '14 at 14:05
1

Get Username and password

Make it more clear to read but put it on a better position over the screen

#!/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
Community
  • 1
  • 1
MikeW
  • 11
  • 1
1

A variation on both @SiegeX and @mklement0's excellent contributions: mask user input; handle backspacing; but only backspace for the length of what the user has input (so we're not wiping out other characters on the same line) and handle control characters, etc... This solution was found here after so much digging!

#!/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
nooblag
  • 678
  • 3
  • 23