0

I have a password function that I borrowed from How do I echo stars (*) when reading password with read?

I tried to adapt it so that I can run through the function twice to do a password confirmation and then evaluate the 2 passwords to determine if they match but I seem to be missing some basics of how bash works in this case.

I tried replacing PASSWORD with $1 but kept getting command not found errors

passWord() {
  unset PASSWORD
  unset CHARCOUNT
  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
  ${1}=${PASSWORD}
}

echo -n "Enter the password > "
passWord passOne
echo -n "Please re-enter the password > "
passWord passTwo
if [[ $passOne == $passTwo ]]; then
   PASSWORD=$passOne
else
   echo "Passwords did not match, please try again."
fi

Update Here is the script with the latest updates

#!/bin/bash
passWord() {
  unset password
  local prompt char
  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
}

echo -n "Enter the password > "
passWord
pass1=$password
echo -n "Please re-enter the password > "
passWord
pass2=$password
if [[ "$pass1" == "$pass2" ]]; then
  PassWord=$pass1
else
  echo "Passwords did not match, please try again."
fi
Community
  • 1
  • 1
bc81
  • 147
  • 1
  • 10
  • What does [**Shellcheck.net**](http://www.shellcheck.net/) tell you about your code? – David C. Rankin May 17 '17 at 17:28
  • @David some warning about globbing and word splitting, but doesn't fix my issue. What are you alluding to? – bc81 May 17 '17 at 17:59
  • The problem is `${1}=${PASSWORD}` does NOT assign `${PASSWORD}` to either `passOne` or `passTwo`. (you cannot do variable assignment that way without `eval` -- not recommended) Replace with `echo ${PASSWORD}` and then outside do something like `passOne=$(passWord)` and the same for `passTwo`. The key is shellcheck telling you `passOne` and `passTwo` are **Referenced but Unassigned** – David C. Rankin May 17 '17 at 18:06
  • @DavidC.Rankin Got it, thanks. – bc81 May 17 '17 at 18:12
  • You should also use `local PROMPT` (or `unset PROMPT`) at the top of your function to prevent the echoing a `*` at the beginning of the second password prompt. – David C. Rankin May 17 '17 at 19:26

1 Answers1

2

You are missing a declaration of your shell.
Please add a shebang as the first line:

#!/bin/bash

The assignment of variables (the line ${1}=${PASSWORD}) doesn't work.
One way to solve it (not recomended) is to add eval:

eval "${1}=${PASSWORD}"     # don't use quite risky.

But as that makes any input a security issue, you should use some other line.

One solution is to use declare (bash 4.2+):

declare -g "${1}=${PASSWORD}"

The -g is required (required and available since bash 4.2) to change General variables (not local to the function).

Or use printf (since bash 3.1):

printf -v "${1}" '%s' "${PASSWORD}"

Other than that, you should add a local command for variables used inside the function to avoid conflicts with external variables and should add a PROMPT='' just before the loop to avoid the printing of an initial asterisk when calling the function a second time.

It should be said that using variables in CAPS should be avoided. Variables in CAPS denote environment variables, the rest of variables use lower case to avoid conflicts.

done
  • 6,370
  • 27
  • 49
  • All good solutions, I've always preferred to output from the function and use a *process substitution*, (e.g. `passOne=$(passWord)`), but there is nothing wrong with the `printf -v` or `declare -g`. – David C. Rankin May 17 '17 at 19:29
  • @DavidC.Rankin Well, a `printf -v` avoids the delay of starting (and closing) an additional sub-shell caused by the `$(…)`. The delay is usually small, but measurable (usually about ~1 msec ). – done May 17 '17 at 19:32