7

I'm pretty new at shell scripts, but I would like to write a basic script where the bash file will echo a different line of text depending on the user input. For example if the script asks the user "Are you there?" and the user input is "yes" or "Yes" then the script would echo something like "hello!". But if the user input is "no" or "No" the script would echo something else. And finally, if the user input is something other than yes/Yes or no/No, the script would echo "Please answer yes or no". Here is what I have so far:

echo "Are you there?"  
read $input  

if [ $input == $yes ]; then  
    echo "Hello!"  
elif [ $input == $no ]]; then  
    echo "Are you sure?"  
else  
    echo "Please answer yes or no."  
fi  

However, no matter the input, I always get the first response ("Hello!")

Also, I would like to incorporate text to speech (as I have done with other bash file projects using festival). In other bash files I have done it this way:

echo "Hello!"  
echo "Hello!" | festival --tts

Is there a way to incorporate this into the if then/yes no prompt above? Thank you in advance, I'm using this to make simple bash files and to help myself learn.

  • 1
    Use Shellcheck to debug shell scripts (also available in the eponymous package). It prints a note about the particular issue(s) in your script. – David Foerster May 20 '18 at 8:53
  • Also good idea is to run script with set -x on top to see diagnostic output. – Sergiy Kolodyazhnyy May 20 '18 at 8:55
13

The main problem is here:

read $input

In bash, usually, $foo is the value of the variable foo. Here, you don't want the value, but the name of the variable, so it should be just:

read input

Similarly, in the if tests, $yes and $no should be just yes and no, since you just want the strings yes and no there.

You could use a case statement here, which (IMHO) makes it easier to do multiple cases based on input:

case $input in
[Yy]es)    # The first character can be Y or y, so both Yes and yes work
    echo "Hello!"  
    echo "Hello!" | festival --tts
    ;;
[Nn]o)     # no or No
    echo "Are you sure?"
    echo "Are you sure?" | festival --tts
    ;;
*)         # Anything else
    echo "Please answer yes or no."
    echo "Please answer yes or no." | festival --tts
    ;;
esac

You could wrap the two echo statements and the use of festival in a function to avoid repeating yourself:

textAndSpeech ()
{
    echo "$@"
    echo "$@" | festival --tts
}
case $input in
[Yy]es)    # The first character can be Y or y, so both Yes and yes work
    textAndSpeech "Hello!"  
    ;;
[Nn]o)     # no or No
    textAndSpeech "Are you sure?"
    ;;
*)         # Anything else
    textAndSpeech "Please answer yes or no."
    ;;
esac

With $input, bash replaces this with its value, which is nothing initially, so the read command run is:

read 

And read by default stores the input in the variable REPLY. So you can, if you want, eliminate the input variable altogether and use $REPLY instead of $input.


Also have a look at the select statement in Bash.

  • Thank you so much! I've been looking all over for this simple solution, but couldn't find it. My only other question is how would I make it a loop? For example, make the script keep looping until the user responds "yes" instead of just ending after the given answer. Thank you so much! – Santiago May 20 '18 at 19:33
  • @Santiago make it a while true; do...done loop, with break statement right after echo "Hello!". – Sergiy Kolodyazhnyy May 20 '18 at 20:56
  • @Santiago that is exactly what the select statement is for! A select statement will repeatedly ask for the user to select one of the available options, and then based on those options you can decide to break out of the select statement or continue looping. See, example: paste.ubuntu.com/p/Ztp3XGCKtF – Olorin May 21 '18 at 6:20
14

TL;DR: fix syntax errors, ensure you actually have non-empty variables in [ test, and make a function to pipe through tee into festival.

As far as printing to screen and outputting to festival, I personally would wrap the script into a function and pipe everything to festival, with tee in between (which sends text both to screen and pipe).

There's three syntax problems to fix, however:

  • read $input should be read input. The $ symbol dereferences variable in shell scripting (i.e., it $input will be replaced with what input holds).
  • You've no yes and no variables declared. What you should be doing is string comparison: [ "$input" == "yes" ]
  • Extra ]] in elif

As for why you get Hello! all the time, that's exactly because of first two bullet points. Before read $input the variable input doesn't exist, so you're only performing read (i.e., nonexistent variable $input is dereferenced into empty string, leaving only read command there). So whatever you type gets stored in the REPLY variable which is what read uses when no variable name has been given. And because yes variable doesn't exist, that gets replaced with empty string too. Thus in reality [ $input == $yes ] is treated as [ "" == "" ] which is always true.

$ [ $nonexistent == $anothernonexistent  ] && echo "Success"
Success

The fixed script should be as so:

#!/bin/bash
main(){
    read -p "Are you there?" input  

    if [ "$input" == "yes" ]; then  
        echo "Hello!"  
    elif [ "$input" == "no" ]; then  
        echo "Are you sure?"  
    else  
        echo "Please answer yes or no."  
    fi 
}
main | tee /dev/tty | festival --tts

Remember to quote the variables and read up on difference between = and == in test command.

  • Can tee be used to pipe cmatrix into festival? ..just don't ask what for. – Willtech May 20 '18 at 12:07
  • @Willtech Well, yes, it can. All the characters cmatrix displays seem to be from the portable character set, so I don't see a problem with other locale characters as in the "real" matrix flow. But I'm not sure how festival would work with that, it'd be just whole lot of gibberish. But in theory it'd work just fine. – Sergiy Kolodyazhnyy May 20 '18 at 16:23
2

A third approach would be to assign to a common variable for each case, and in the end act upon that variable. I'm not sure how common this approach is in bash, but in other programming languages it's fairly standard.

Also shopt -s nocasematch in conjunction with [[ for case-insensitive string comparison.
With this also YES and NO are treaded as valid input.

shopt
[[

#!/bin/bash

shopt -s nocasematch

read -p "Are you there?" input  

if [[ "$input" == "yes" ]]; then  
    msg="Hello!"  
elif [[ "$input" == "no" ]]; then  
    msg="Are you sure?"  
else  
    msg="Please answer yes or no."  
fi 

echo $msg
echo $msg | festival --tts

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.