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

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! 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!

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

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
Owner

davejamesmiller commented Aug 19, 2014

@swiftarrow - Updated, thanks

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).

Owner

davejamesmiller commented Mar 25, 2015

@carlor - Done, thanks for sharing

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

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

thanks did the option 1.thanks for your update.

Thank you very much!!

Very simple and very effective 👍

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
        ...
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