2

My bash script is supposed to get user input to deal with a particular file exiting as in:

if [ -e "formatted_log.csv" ]
then
    printf "\nPresence of formatted_log.csv file detected: It appears this script has already been run is this dir.\n"
    printf "Appending can cause errors.\n"
    read -e -p "Would you like to continue?[y/n]" stdin

This logic checks the user's input:

if [ $stdin = "n" ] || [ $stdin = "N" ];
then 
printf "\nOkay, exiting.\n"
exit;
fi
if [ $stdin != "y" ]&&[ $stdin != "Y" ]&&[ $stdin != "n" ]&&[ $stdin != "N" ]; 
then 
printf "\nPlease use a valid input (y/n)...exiting.\n"
exit;
    fi
fi

The issue is that if you just press enter the script executes as though a "y" or a "Y" have been input, and I don't know why. My understanding is that the way this is written, it should exit if the user puts in anything other than a y,Y,n,or N.

It prints this when you have no input:

master_script.sh: line 14: [: =: unary operator expected
master_script.sh: line 14: [: =: unary operator expected
master_script.sh: line 19: [: !=: unary operator expected

But doesn't exit – how can I make it exit?

4
  • Switching to the [[ ]] form of conditional expressions will probably get you there, from a quick look. Also make sure every [ ] && || and the like has spaces around it. shellcheck can help you. Commented Feb 23, 2017 at 17:35
  • There's no point if telling the user they gave an invalid input if you are just going to exit anyway (as if they typed n or N). Commented Feb 23, 2017 at 17:55
  • 1
    test -f is not checking for existence - it's much more picky than that. You need either to write -e or change your description. Commented Feb 23, 2017 at 18:38
  • @TobySpeight Thank you- I didn't even realize this. Commented Feb 25, 2017 at 18:10

2 Answers 2

4

Since you've tagged this with "Bash", you should have the more powerful [[ ]] operator at your disposal. You could then simplify to something like this:

read stdin

if [[ $stdin == [Nn] ]]; then
    echo "Exiting"
    exit
fi

if [[ $stdin != [YyNn] ]]; then
    echo "Invalid input, exiting"
    exit
fi

== (or =) and != in [[ ]] perform pattern matching, so you can use patterns to check if your input is valid, in a single expression like [YyNn].

If you want to ask for input until the user enters something valid, you could loop like this:

while [[ $stdin != [YyNn] ]]; do
    read -p 'Continue? ' stdin
done

Notice that while it's almost always good practice to quote your variables in Bash, you don't have to within [[ ]]. And if your pattern is in a variable, it actually must not be quoted, or it isn't interpreted as a pattern.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the additional hint of using pattern matching!
2

The problem is NOT having the quotes around the variable $stdin which is not retained with an empty value when quotes are not present,

For example, the [...] sees the expressions as below when the variable is empty,

+ '[' = n ']'
script.sh: line 9: [: =: unary operator expected
+ '[' = N ']'
script.sh: line 9: [: =: unary operator expected
+ '[' '!=' y ']'
script.sh: line 14: [: !=: unary operator expected
+ '[' 0 eq 0 ']'

You need to properly quote them, to make it work, in line

9 if [ "$stdin" = "n" ] || [ "$stdin" = "N" ];

and

14  if [ "$stdin" != "y" ] && [ "$stdin" != "Y" ] && [ "$stdin" != "n" ] && [ "$stdin" != "N" ];

with this, the enter key press are safely handled as,

+ '[' '' = n ']'
+ '[' '' = N ']'
+ '[' '' '!=' y ']'
+ '[' '' '!=' Y ']'
+ '[' '' '!=' n ']'
+ '[' '' '!=' N ']'

with the above changes running the full script and pressing Enter on the prompt, in debugger mode,

 bash -x script.sh 
+ '[' -f file ']'
+ printf '\nPresence of formatted_log.csv file detected: It appears this script has already been run is this dir.\n'

Presence of formatted_log.csv file detected: It appears this script has already been run is this dir.
+ printf 'Appending can cause errors.\n'
Appending can cause errors.
+ read -e -p 'Would you like to continue?[y/n]' stdin
Would you like to continue?[y/n]
+ '[' '' = n ']'
+ '[' '' = N ']'
+ '[' '' '!=' y ']'
+ '[' '' '!=' Y ']'
+ '[' '' '!=' n ']'
+ '[' '' '!=' N ']'
+ printf '\nPlease use a valid input (y/n)...exiting.\n'

Please use a valid input (y/n)...exiting.
+ exit

As a healthy alternate you can just use the negative regex match on the list of allowed prompts as below

if [[ ! $stdin =~ ^(y|Y|n|N)$ ]]

Another efficient way to just check for just an empty string on the variable,

if [ "$stdin" = "" ]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.