Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Bash General-Purpose Yes/No Prompt Function ("ask")
# This is a general-purpose function to ask Yes/No questions in Bash, either
# with or without a default answer. It keeps repeating the question until it
# gets a valid answer.
ask() {
# https://djm.me/ask
local prompt default reply
while true; do
if [ "${2:-}" = "Y" ]; then
prompt="Y/n"
default=Y
elif [ "${2:-}" = "N" ]; then
prompt="y/N"
default=N
else
prompt="y/n"
default=
fi
# Ask the question (not using "read -p" as it uses stderr not stdout)
echo -n "$1 [$prompt] "
# Read the answer (use /dev/tty in case stdin is redirected from somewhere else)
read reply </dev/tty
# Default?
if [ -z "$reply" ]; then
reply=$default
fi
# Check if the reply is valid
case "$reply" in
Y*|y*) return 0 ;;
N*|n*) return 1 ;;
esac
done
}
# EXAMPLE USAGE:
if ask "Do you want to do such-and-such?"; then
echo "Yes"
else
echo "No"
fi
# Default to Yes if the user presses enter without giving an answer:
if ask "Do you want to do such-and-such?" Y; then
echo "Yes"
else
echo "No"
fi
# Default to No if the user presses enter without giving an answer:
if ask "Do you want to do such-and-such?" N; then
echo "Yes"
else
echo "No"
fi
# Only do something if you say Yes
if ask "Do you want to do such-and-such?"; then
said_yes
fi
# Only do something if you say No
if ! ask "Do you want to do such-and-such?"; then
said_no
fi
# Or if you prefer the shorter version:
ask "Do you want to do such-and-such?" && said_yes
ask "Do you want to do such-and-such?" || said_no
@tomm174

This comment has been minimized.

Show comment Hide comment
@tomm174

tomm174 Jan 10, 2013

Yes ! - thank you.
This is just right - there are a lot of egos out there showing how 'clever' they are by making it over-complicated.
This does just what is required for a fn that prompts and can except Y or N as default, and the usage examples are clear

tomm174 commented Jan 10, 2013

Yes ! - thank you.
This is just right - there are a lot of egos out there showing how 'clever' they are by making it over-complicated.
This does just what is required for a fn that prompts and can except Y or N as default, and the usage examples are clear

@dkirrane

This comment has been minimized.

Show comment Hide comment
@dkirrane

dkirrane May 20, 2013

Hi,

This function is exactly what I needed.

But I'd also like to add a batch option for it.
So, if -B is set the ask function will not prompt and just use the default. Any advice how best do this?

Hi,

This function is exactly what I needed.

But I'd also like to add a batch option for it.
So, if -B is set the ask function will not prompt and just use the default. Any advice how best do this?

@swiftarrow

This comment has been minimized.

Show comment Hide comment
@swiftarrow

swiftarrow Feb 2, 2014

Hi! Thanks alot for this function!

One thing you might like to fix: http://www.shellcheck.net/ reports that the keyword function is optional in bash and incompatible with other shells. Also, there should be brackets () after the function name.

Thanks!

Hi! Thanks alot for this function!

One thing you might like to fix: http://www.shellcheck.net/ reports that the keyword function is optional in bash and incompatible with other shells. Also, there should be brackets () after the function name.

Thanks!

@Willscrlt

This comment has been minimized.

Show comment Hide comment
@Willscrlt

Willscrlt Jul 25, 2014

I was working on something similar to this that included a timeout, and your snippet gave me some helpful inspiration. Thanks!

I was working on something similar to this that included a timeout, and your snippet gave me some helpful inspiration. Thanks!

@davejamesmiller

This comment has been minimized.

Show comment Hide comment
@davejamesmiller

davejamesmiller Aug 19, 2014

@dkirrane: I normally do something like this:

if [ "$1" = "-B" ]; then
  batch=true
else
  batch=false
fi

if $batch || ask "Prompt"; then
  # Do something
fi

Or if you're only asking one question:

if [ "$1" = "-B" ] || ask "Prompt"; then
  # Do something
fi
Owner

davejamesmiller commented Aug 19, 2014

@dkirrane: I normally do something like this:

if [ "$1" = "-B" ]; then
  batch=true
else
  batch=false
fi

if $batch || ask "Prompt"; then
  # Do something
fi

Or if you're only asking one question:

if [ "$1" = "-B" ] || ask "Prompt"; then
  # Do something
fi
@davejamesmiller

This comment has been minimized.

Show comment Hide comment
@davejamesmiller

davejamesmiller Aug 19, 2014

@swiftarrow - Updated, thanks

Owner

davejamesmiller commented Aug 19, 2014

@swiftarrow - Updated, thanks

@caelor

This comment has been minimized.

Show comment Hide comment
@caelor

caelor Dec 10, 2014

I've forked and made a minor amendment that you might want to include as a revision - it forces the use of /dev/tty (the controlling terminal) as the input to the read. That way, the function can be called within loops that use stdin for something else (like reading files).

caelor commented Dec 10, 2014

I've forked and made a minor amendment that you might want to include as a revision - it forces the use of /dev/tty (the controlling terminal) as the input to the read. That way, the function can be called within loops that use stdin for something else (like reading files).

@davejamesmiller

This comment has been minimized.

Show comment Hide comment
@davejamesmiller

davejamesmiller Mar 25, 2015

@carlor - Done, thanks for sharing

Owner

davejamesmiller commented Mar 25, 2015

@carlor - Done, thanks for sharing

@dtigue

This comment has been minimized.

Show comment Hide comment
@dtigue

dtigue Apr 12, 2015

Perfect! I can't tell you how many of my scripts will include your function from now on. Thanks!

dtigue commented Apr 12, 2015

Perfect! I can't tell you how many of my scripts will include your function from now on. Thanks!

@muvvas

This comment has been minimized.

Show comment Hide comment
@muvvas

muvvas May 6, 2016

Hi Dave
Thanks for the script
in my script i m using debug and error redirect using exec 2 >/tmp/somefile
ex

/bin/bash

set -x
exec 2>/tmp/somefile
here askfunction{}
if ask "do you want to install ?"; then
do something
fi

when i run the script,its not asking the Question on console,its going to log file
will you pleas help me how can i use set -x and exec 2 ,,to get the only debuging output to file and ask question on console

Thanks,
Srikanth

muvvas commented May 6, 2016

Hi Dave
Thanks for the script
in my script i m using debug and error redirect using exec 2 >/tmp/somefile
ex

/bin/bash

set -x
exec 2>/tmp/somefile
here askfunction{}
if ask "do you want to install ?"; then
do something
fi

when i run the script,its not asking the Question on console,its going to log file
will you pleas help me how can i use set -x and exec 2 ,,to get the only debuging output to file and ask question on console

Thanks,
Srikanth

@davejamesmiller

This comment has been minimized.

Show comment Hide comment
@davejamesmiller

davejamesmiller May 6, 2016

@muvvas:

Two possibilities:

  1. Output the prompt yourself:

    echo -n "$1 [$prompt] "
    read REPLY </dev/tty
  2. Redirect the prompt from stderr to stdout:

    read -p "$1 [$prompt] " REPLY 2>&1 </dev/tty

Personally I prefer the first one even though it's one more line. I have updated the Gist to use that because it seems more logical to me to use stdout (I never realised it uses stderr).

http://stackoverflow.com/questions/5983988/bash-read-and-stderr-redirection

Owner

davejamesmiller commented May 6, 2016

@muvvas:

Two possibilities:

  1. Output the prompt yourself:

    echo -n "$1 [$prompt] "
    read REPLY </dev/tty
  2. Redirect the prompt from stderr to stdout:

    read -p "$1 [$prompt] " REPLY 2>&1 </dev/tty

Personally I prefer the first one even though it's one more line. I have updated the Gist to use that because it seems more logical to me to use stdout (I never realised it uses stderr).

http://stackoverflow.com/questions/5983988/bash-read-and-stderr-redirection

@muvvas

This comment has been minimized.

Show comment Hide comment
@muvvas

muvvas May 9, 2016

thanks did the option 1.thanks for your update.

muvvas commented May 9, 2016

thanks did the option 1.thanks for your update.

@empoisoner

This comment has been minimized.

Show comment Hide comment
@empoisoner

empoisoner May 31, 2016

Thank you very much!!

Very simple and very effective 👍

Thank you very much!!

Very simple and very effective 👍

@bobwest

This comment has been minimized.

Show comment Hide comment
@bobwest

bobwest Aug 3, 2016

I love the simplicity -- great work.

I localize the variables at the start of the function to avoid polluting the environment in larger scripts:

ask() {
    # http://djm.me/ask
    local prompt default REPLY
    while true; do
        ...

bobwest commented Aug 3, 2016

I love the simplicity -- great work.

I localize the variables at the start of the function to avoid polluting the environment in larger scripts:

ask() {
    # http://djm.me/ask
    local prompt default REPLY
    while true; do
        ...
@davejamesmiller

This comment has been minimized.

Show comment Hide comment
@davejamesmiller

davejamesmiller Aug 3, 2016

@bobwest Good idea, I've added that to the Gist.

Owner

davejamesmiller commented Aug 3, 2016

@bobwest Good idea, I've added that to the Gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment