Create a gist now

Instantly share code, notes, and snippets.

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() {
# http://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
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

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

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

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

@davejamesmiller
Owner

@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
Owner

@swiftarrow - Updated, thanks

@caelor
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
Owner

@carlor - Done, thanks for sharing

@dtigue
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
muvvas commented May 6, 2016 edited

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
Owner
davejamesmiller commented May 6, 2016 edited

@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
muvvas commented May 9, 2016

thanks did the option 1.thanks for your update.

@empoisoner

Thank you very much!!

Very simple and very effective 👍

@bobwest
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
Owner

@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