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
  • 177
  • 2
  • 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.

  • 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 ). –  May 17 '17 at 19:32