29

How do I ask a yes/no type question in Bash?

I ask the question... echo "Do you like pie?"

And receive the answer... read pie

How do I do something if the answer is yes, or starts with y (so yes and yeah, etc, will work too).

2
  • 2
    For zsh users, the -q option is available. Read only one character from the terminal and set name to 'y' if this character was 'y' or 'Y' and to 'n' otherwise. With this flag set the return status is zero only if the character was 'y' or 'Y'. Note that this always reads from the terminal, even if used with the -p or -u or -z flags or with redirected input. This option may also be used within zle widgets. Commented May 15, 2019 at 16:51
  • 1
    @UlysseBN zsh never ceases to amaze me! I don't remember what this original question was for, but I'm going to keep that in mind for when I have control of the shell executing my script - thank you! Commented May 17, 2019 at 16:01

6 Answers 6

41

I like to use the following function:

function yes_or_no {
    while true; do
        read -p "$* [y/n]: " yn
        case $yn in
            [Yy]*) return 0  ;;  
            [Nn]*) echo "Aborted" ; return  1 ;;
        esac
    done
}

So in your script you can use like this:

yes_or_no "$message" && do_something

In case the user presses any key other than [yYnN] it will repeat the message.

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

6 Comments

read -p "$@ [y/n]: " is incorrect, you need to use $* or the read will explode if the function is called with more than one argument. Also, technically this should use yes_or_not "$@" but for this that only matters if you use yes_or_not 'foo bar' and then the user doesn't input yes or no (the inner spaces will then get lost).
That's pretty awesome :)
I added a question mark after the $* ... This also works great with an if block: if yes_or_no "Do next task"; then ;;;; fi constructs
I would make yn a local variable, just to avoid polluting the global scope...
Can someone elaborate on how the succeed/fail is handled here? yes_or_no $message && do_something I'm assuming if (0) was returned then && do_something executes for the (Yy) response, but what if they type (Nn)? I see the comment by phyatt, but I don't understand why he put ;;;; after then
|
16

This works too:

read -e -p "Do you like pie? " choice
[[ "$choice" == [Yy]* ]] && echo "doing something" || echo "that was a no"

Pattern starting with Y or y will be taken as yes.

5 Comments

What are the reasons to include the -e and -p flags out of curiosity?
@twhitney see read --help for details.
read: bad option: -h Nice.
You can find some more in this blog: linuxscrew.com/bash-prompt-for-input
-e -- input read is echoed and not assigned Not sure about -p. Allegedly, "Output the string PROMPT without a trailing newline before attempting to read," from the above link. But I didn't think it it produced a new line after the prompt anyway.
16

I like Jahid's oneliner. Here is a slight simplification of it:

[[ "$(read -e -p 'Continue? [y/N]> '; echo $REPLY)" == [Yy]* ]]

Here are some tests:

$ [[ "$(read -e -p 'Continue? [y/N]> '; echo $REPLY)" == [Yy]* ]] && echo Continuing || echo Stopping

Continue? [y/N]> yes
Continuing

$ for test_string in y Y yes YES no ''; do echo "Test String: '$test_string'"; echo $test_string | [[ "$(read -e -p 'Continue? [y/N]>'; echo $REPLY)" == [Yy]* ]] && echo Continuing || echo Stopping; done

Test String: 'y'
Continuing

Test String: 'Y'
Continuing

Test String: 'yes'
Continuing

Test String: 'YES'
Continuing

Test String: 'no'
Stopping

Test String: ''
Stopping

Update

In response to a comment, I'm going to add an adaptation to make this work in zsh.

Disclaimer

I would never write a shell script in zsh even though it is now my primary interactive shell. I still write all scripts in bash or sh. However, since you sometimes need to script modifications to your interactive shell (ex: source ~/dev/set_env), you might want to include prompting.

#! /usr/bin/env zsh
[[ "$(echo -n 'Continue? [y/N]> ' >&2; read; echo $REPLY)" == [Yy]* ]] \
  && echo Continuing \
  || echo Stopping

3 Comments

this only works on bash read not zsh read.
@Akhil I added a zsh example for you. ☮️❤️🌈
appending ? worked for me in zsh. i.e. [[ "$(read -e '?Continue? [y/N]> '; echo $REPLY)" == [Yy]* ]] ref: superuser.com/questions/555874/…
6

This works:

echo "Do you like pie?"
read pie
if [[ $pie == y* ]]; then
    echo "You do! Awesome."
else
    echo "I don't like it much, either."
fi

[[ $pie == y* ]] tests to see of the variable $pie starts with y.

Feel free to make this better if you'd like.

Comments

5

In contrast to the other answers this function gives you the possibility to set a default:

function askYesNo {
        QUESTION=$1
        DEFAULT=$2
        if [ "$DEFAULT" = true ]; then
                OPTIONS="[Y/n]"
                DEFAULT="y"
            else
                OPTIONS="[y/N]"
                DEFAULT="n"
        fi
        read -p "$QUESTION $OPTIONS " -n 1 -s -r INPUT
        INPUT=${INPUT:-${DEFAULT}}
        echo ${INPUT}
        if [[ "$INPUT" =~ ^[yY]$ ]]; then
            ANSWER=true
        else
            ANSWER=false
        fi
}

askYesNo "Do it?" true
DOIT=$ANSWER

if [ "$DOIT" = true ]; then
    < do some stuff >
fi

On the command line you would see

Do it? [Y/n] y

Comments

0

Here is a short function:

prompt(){ read -p "$1" a; [ $a = "y" ]; }

Usage (if the answer is y then do_something executed):

prompt "Do you want it?"  &&  do_something

Usage with multiple commands:

prompt "Do you want it?" && {
  do_something1
  do_something2
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.