0

I am trying to use a while loop to repeatedly prompt for a username during user registration until a valid username has been provided (i.e. it has NOT already been claimed).

Unfortunately, I've only been coding in bash for less than 70 hours now, so I wasn't familiar with how to do this. My initial thought was to use "goto" like in Windows batch scripts if there was a problem with the username, but apparently that's not a thing in bash and loops seemed to be the recommended route. I wasn't familiar with loops in bash so I did some research until I found a good answer on SO about how to do this. This isn't the exact post (I can't find it now), but I was looking at questions like this one.

The resulting code looks like this:

echo -e "Now, choose a unique username. No special characters, please."
uniqueuser=0 # Assume a valid username has not been chosen from the get-go
while [ "$uniqueuser" == "0" ]
do
    read -p "Username: " username
    lusername=`echo $username | sed y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/` #lowercase username
    if [ "$lusername" == "admin" ] || [ "$lusername" == "administrator" ] || [ "$lusername" == "npstn" ] || [ "$lusername" == "phreak" ] || [ "$lusername" == "guest" ] || [ "$lusername" == "user" ] || [ "$lusername" == "sysop" ]
    then
        echo -e "That username is reserved. Please pick another username."
    else
        username=`php /home/com/encode.php "$username"`
        available=`curl "https://example.com/redacted/redacted.php?key=$key&type=checkusername&username=$username" --silent`
        if [ "$available" == "1" ]
        then
            uniqueuser=$((1)) # Username is unique and acceptable
        else
            echo -e "That username is taken. Please choose another one."
        fi
    fi
done <input.data # so that username variable persists outside of while loop
echo -e "That username is available... or was... now, it's yours!"
echo -e "On this board, we have no annoying password length or complexity requirements. That said, your password cannot be null, nor can it use the plus symbol. Choose wisely!"

When this part of the shell script is reached, I see:

Now, choose a unique username. No special characters, please.
/home/com/redacted.sh: line 4: input.data: No such file or directory
That username is available... or was... now, it's yours!
On this board, we have no annoying password length or complexity requirements. That said, your password cannot be null, nor can it use the plus symbol. Choose wisely!

The reason "input.data" is used is to get the $username variable out from the subshell and back into the main shell. However, that seems to be causing an error. How can I fix this?

InterLinked
  • 1,247
  • 2
  • 18
  • 50
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/195386/discussion-on-question-by-interlinked-return-variable-from-while-loop-in-bash). – Samuel Liew Jun 22 '19 at 01:25
  • Don't use backticks. If you want to record the output of a command in a variable, use `var=$(cmd)` – William Pursell Jun 22 '19 at 03:01
  • Don't use `sed y/` to translate characters. You can convert to lowercase with tr: `lusername=$( echo "$username" | tr '[:upper:]' '[:lower:]')` – William Pursell Jun 22 '19 at 03:03
  • Why are you defining `uniqueuser` at all? Just do `while read username` and include a `break` statement when you find a valid name. – William Pursell Jun 22 '19 at 03:06
  • @WilliamPursell, Assuming it's a recent version of `bash` just do `lusername="${username,,}"`. – agc Jun 22 '19 at 07:23

1 Answers1

3

You've misread the sources on which you're relying. Redirecting stdin from a file, as in:

count=0
while IFS= read -r line; do
  (( ++count )) # or do something else
done < input.data
echo "$count"   # will print number of lines in the file input.data

only prevents a subshell from being created when the alternative is redirecting stdin from a pipe. For example, you would indeed lose the content of variables you set in:

# DO NOT DO THIS; this is the practice the other answers were teaching how to avoid.
count=0
cat input.data | while IFS= read -r line; do
  (( ++count )) # or otherwise modify data within the loop
done
echo "$count"   # will print 0, because the loop was in a subshell

This change is not necessary, and not appropriate, when your input is from stdin and not from a file.

It's the cat input.data | that is a problem, for which < input.data is a replacement. If your original code had no cat input.data |, then you need not use < input.data to replace it.


Demonstrating that a while read loop can read from stdin, and retain its variables' values, without any such redirection:

count=0
prompt='Enter a string, or a blank line to stop: '
while IFS= read -r -p "$prompt" line && [[ $line ]]; do
  (( ++count ))
  printf 'Line %d: %s\n' "$count" "$line"
done
echo "Final counter is $count"

...keeps the same value for $count outside the loop it hand inside the loop, when a blank line is entered.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441