I want to put a quick "are you sure?" prompt for confirmation at the top of a potentially dangerous bash script, what's the easiest/best way to do this?
read -p "Are you sure? " -n 1 -r
echo # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
# do dangerous stuff
fi
I incorporated levislevis85's suggestion (thanks!) and added the -n
option to read
to accept one character without the need to press Enter. You can use one or both of these.
Also, the negated form might look like this:
read -p "Are you sure? " -n 1 -r
echo # (optional) move to a new line
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
[[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1 # handle exits from shell or function but don't exit interactive shell
fi
However, as pointed out by Erich, under some circumstances such as a syntax error caused by the script being run in the wrong shell, the negated form could allow the script to continue to the "dangerous stuff". The failure mode should favor the safest outcome so only the first, non-negated if
should be used.
Explanation:
The read
command outputs the prompt (-p "prompt"
) then accepts one character (-n 1
) and accepts backslashes literally (-r
) (otherwise read
would see the backslash as an escape and wait for a second character). The default variable for read
to store the result in is $REPLY
if you don't supply a name like this: read -p "my prompt" -n 1 -r my_var
The if
statement uses a regular expression to check if the character in $REPLY
matches (=~
) an upper or lower case "Y". The regular expression used here says "a string starting (^
) and consisting solely of one of a list of characters in a bracket expression ([Yy]
) and ending ($
)". The anchors (^
and $
) prevent matching longer strings. In this case they help reinforce the one-character limit set in the read
command.
The negated form uses the logical "not" operator (!
) to match (=~
) any character that is not "Y" or "y". An alternative way to express this is less readable and doesn't as clearly express the intent in my opinion in this instance. However, this is what it would look like: if [[ $REPLY =~ ^[^Yy]$ ]]
[]
including the regex match operator =~
. See: mywiki.wooledge.org/BashFAQ/031 –
Muscadine read -p "Are you sure? " -n 1 -r REPLY
–
Tammeratammi $REPLY
is automatically set if no variable name is supplied. –
Muscadine -r
option tells read
not to interpret backslashes as escape characters. I understand it can also be used on the negated form above. –
Bullfinch sh
shell there is a risk that the read and conditional [[ will fail but the script will continue without exiting - perhaps not want you want - the positive version is therefore safer –
Asthenic sh
doesn't support either [[
or =~
. –
Muscadine [[: not found
for both the negated and non-negated forms when run under sh
. Which sh
are you running? Oh, now I see what you mean. You're not talking about the syntax, but the logic. Yes, your point is very much valid! –
Muscadine [[ $REPLY =~ ^[Yy]$ ]] || exit -1
How would this be evaluated by an older shell? –
Jaredjarek $REPLY
is the default variable for read
and regex matching. Taken together, they are supported by Bash >= 3.2, ksh 93 (at least) and zsh (I don't know the version history). The ||
(logical or) construct is supported by most versions of most shells. –
Muscadine echo ""
just after fi
? This will send to a new line the cursor, it's just for a better user experience :) Before this, the result output was: Are you sure? nroot@server:
–
Landahl =~ ^[Yy]$
is doing/means in the if statement? if [[ $REPLY =~ ^[Yy]$ ]]
–
Gorgonian =~
is Bash's regex match operator. ^[Yy]$
is a regular expression which means a string which consists only of an upper or lower case y. –
Muscadine read -p "Are you sure? " -r -i y -e
but the user would need to backspace to be able to change the "y" to an "n", for example. For Bash 3.2 and later, change the pattern in the match in the first example in my answer to ^([Yy]|)$
- this means to match upper or lower case "Y" or nothing (a blank line). –
Muscadine yellow=$(tput setaf 3); reset=$(tput sgr0); read -p "${yellow}Name: ${reset}"
See man 5 terminfo
in the Color Handling section for more information. Note that if you use read -e
for readline support, you should use \001
and \002
to wrap the control sequences like this: yellow=$(tput setaf 3); reset=$(tput sgr0); printf -v prompt "\001${yellow}\002Name: \001${reset}\002"; read -e -p "$prompt"
–
Muscadine =~' ,
if [[ ! $REPLY =~ ^[Yy]$ ]]' ..... Oh looks like your example is Bash specific stackoverflow.com/questions/11206233/… –
Scala read
with a file that has a /bin/bash
shebang (while using a zsh terminal), this is what worked for me choice=$(bash -c "read -p 'Are you sure? (y/n) ' -n 1 c; echo \$c")
–
Kermie $REPLY
is the builtin variable that read
stores the string it reads in if you don't supply a variable name as an argument. I describe it in my answer in the first paragraph under "Explanation". –
Muscadine #!/bin/sh BRANCH=`git rev-parse --abbrev-ref HEAD` PROTECTED_BRANCH="master" MSG="$1" bug="bug-fix" if [ "$BRANCH" = "$PROTECTED_BRANCH" ]; then echo "inside branch if branch=$BRANCH and protected branch=$PROTECTED_BRANCH" if ! grep -qE "bug-fix" "$MSG"; then cat "$MSG" echo "Your commit message must contain the word 'bug-fix'" exit 1 else exit 0 fi else read -p "Are you sure you want to commit test (y/n) ?" -n 1 -r echo if [ "$REPLY" =~ ^[Yy]$ ] then exit 0 else echo "commit aborted" exit 1 fi fi
–
Pauiie /bin/sh
(Often Dash) usually doesn't support Bash features. In this case, its simpler version of read
doesn't support the -n
option which, in Bash, limits the number of characters that read
accepts. –
Muscadine syntax error near unexpected token
fi'` –
Phenacaine y
, you can make the match group optional with ?
: if [[ $REPLY =~ ^[yY]?$ ]]
–
Sandhurst -r
and -n
' –
Williawilliam [[ ${REPLY^} = Y ]]
is slightly easier to understand than a regex. Good answer though. –
Sodomy read -p "Continue? [Enter] → Yes, [Ctrl]+[C] → No."
. –
Ligulate if [[ $REPLY =~ ^[Yy]$ ]]
is needed to proceed not if [[ ! $REPLY =~ ^[Yy]$ ]]
–
Eleen REPLY
variable gets the value e
from the following echo
then it complains about the unknown cho
>_> –
Pimental read
command would execute immediately, waiting for the next input which is the echo
command. Since the -n
option is set to accept one character then exit, the "e" goes into the REPLY
variable and the rest of the line goes to the shell for execution. Since the "e" was read, the rest is "cho ..." which isn't a recognized command. If you want to try this from the command line, enter the read
command, press a key, then enter the echo command. –
Muscadine if
on to the rest of the script. One of the things this does is prevent one from having to wrap a whole script inside the if
. –
Muscadine read -p "Are you sure? [y/N] " -n 1 -r
And then, echo a newline unless they hit "Enter". [[ -z $REPLY ]] || echo
–
Strafe use case/esac.
read -p "Continue (y/n)?" choice
case "$choice" in
y|Y ) echo "yes";;
n|N ) echo "no";;
* ) echo "invalid";;
esac
advantage:
- neater
- can use "OR" condition easier
- can use character range, eg [yY][eE][sS] to accept word "yes", where any of its characters may be in lowercase or in uppercase.
echo "Quitting script."; exit;;
), but if the input is yes, the script will just continue with whatever comes after esac
? –
Tabular n|N ) echo "no"; return;;
the script will end there if you say 'n' and continue with the rest otherwise, is that what you mean? –
Etam Try the read
shell builtin:
read -p "Continue (y/n)?" CONT
if [ "$CONT" = "y" ]; then
echo "yaaa";
else
echo "booo";
fi
This way you get 'y' 'yes' or 'Enter'
read -r -p "Are you sure? [Y/n]" response
response=${response,,} # tolower
if [[ $response =~ ^(y| ) ]] || [[ -z $response ]]; then
your-action-here
fi
If you are using zsh try this:
read "response?Are you sure ? [Y/n] "
response=${response:l} #tolower
if [[ $response =~ ^(y| ) ]] || [[ -z $response ]]; then
your-action-here
fi
Here's the function I use:
function ask_yes_or_no() {
read -p "$1 ([y]es or [N]o): "
case $(echo $REPLY | tr '[A-Z]' '[a-z]') in
y|yes) echo "yes" ;;
*) echo "no" ;;
esac
}
And an example using it:
if [[ "no" == $(ask_yes_or_no "Are you sure?") || \
"no" == $(ask_yes_or_no "Are you *really* sure?") ]]
then
echo "Skipped."
exit 0
fi
# Do something really dangerous...
- The output is always "yes" or "no"
- It's "no" by default
- Everything except "y" or "yes" returns "no", so it's pretty safe for a dangerous bash script
- And it's case insensitive, "Y", "Yes", or "YES" work as "yes".
This what I found elsewhere, is there a better possible version?
read -p "Are you sure you wish to continue?"
if [ "$REPLY" != "yes" ]; then
exit
fi
[[ -f ./${sname} ]] && read -p "File exists. Are you sure? " -n 1
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
used this in a function to look for an existing file and prompt before overwriting.
echo are you sure?
read x
if [ "$x" = "yes" ]
then
# do the dangerous stuff
fi
#!/bin/bash
echo Please, enter your name
read NAME
echo "Hi $NAME!"
if [ "x$NAME" = "xyes" ] ; then
# do something
fi
I s a short script to read in bash and echo back results.
qnd: use
read VARNAME
echo $VARNAME
for a one line response without readline support. Then test $VARNAME however you want.
© 2022 - 2024 — McMap. All rights reserved.