Instantly share code, notes, and snippets.

Embed
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://gist.github.com/davejamesmiller/1965569
local prompt default reply
if [ "${2:-}" = "Y" ]; then
prompt="Y/n"
default=Y
elif [ "${2:-}" = "N" ]; then
prompt="y/N"
default=N
else
prompt="y/n"
default=
fi
while true; do
# 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.

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.

dkirrane commented 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?

@swiftarrow

This comment has been minimized.

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

@Willscrlt

This comment has been minimized.

Willscrlt commented Jul 25, 2014

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.

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.

Owner

davejamesmiller commented Aug 19, 2014

@swiftarrow - Updated, thanks

@caelor

This comment has been minimized.

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.

Owner

davejamesmiller commented Mar 25, 2015

@carlor - Done, thanks for sharing

@dtigue

This comment has been minimized.

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.

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.

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.

muvvas commented May 9, 2016

thanks did the option 1.thanks for your update.

@empoisoner

This comment has been minimized.

empoisoner commented May 31, 2016

Thank you very much!!

Very simple and very effective 👍

@bobwest

This comment has been minimized.

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.

Owner

davejamesmiller commented Aug 3, 2016

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

@oscarkramer

This comment has been minimized.

oscarkramer commented May 18, 2018

There are times the script will be launched programmatically with no human to answer a prompt. Checking for an interactive shell would be a good addition:

# assign default here

if [ "$(ps -o stat= -p $PPID)" == "Ss" ]; then
    # do ask in here#
fi
@davejamesmiller

This comment has been minimized.

Owner

davejamesmiller commented May 18, 2018

I think you can simplify that to:

if [ -t 0 ] && ask ...; then
	...
fi

https://www.tldp.org/LDP/abs/html/fto.html

Related: https://gist.github.com/davejamesmiller/1966557

@graphicsminded

This comment has been minimized.

graphicsminded commented Jul 13, 2018

Dave, Amazingly clean function. I've also forked and made a minor amendment to your commenting for those who are new to scripting here:

https://gist.github.com/graphicsminded/16669f2fc704682d624b5e39d004259b/revisions#diff-aa8507c8db614cf57eb12d5ada16542c

I simply was new to scripting when I read this and didn't know what you where saying with: # Default?

See if you would like to include this too as a revision for others who are new.

Thanks again Wow!

@davejamesmiller

This comment has been minimized.

Owner

davejamesmiller commented Aug 14, 2018

@graphicsminded I don't like not having the syntax highlighting, but I have split them out into a separate file - is that clearer?

@jRimbault

This comment has been minimized.

jRimbault commented Sep 7, 2018

Thank you for this.

Shouldn't the prompt definition if/else be outside the loop ?

@davejamesmiller

This comment has been minimized.

Owner

davejamesmiller commented Sep 14, 2018

Yes it should 👍

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