I am trying to implement confirmation prompt with a bash script but for some reason, prompt won't wait for user input. I've tried many examples but no luck so far. I am on MacOS if it makes any difference.
Just a few examples I tried (All copy+paste from other answers in SO):
#!/bin/bash
read -p "Are you sure? " -n 1 -r
echo # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
# do dangerous stuff
fi
#!/bin/bash
read -p "Continue (y/n)?" CONT
if [ "$CONT" = "y" ]; then
echo "yaaa";
else
echo "booo";
fi
#!/bin/bash
while true; do
read -rsn1 input
if [ "$input" = "a" ]; then
echo "hello world"
fi
done
#!/bin/bash
read -p "Continue (y/n)?" choice
case "$choice" in
y|Y ) echo "yes";;
n|N ) echo "no";;
* ) echo "invalid";;
esac
This doesn't even prompt anything:
#!/bin/bash
read -n 1 -s -r -p "Press any key to continue"
Changed to answer from comment : in commit-msg hook it seems standard input is closed, indeed this can be checked adding following command
ls -l /dev/fd/
which gives
... 0 -> /dev/null
as mentioned in this post
exec 0< /dev/tty
will restore standard input to tty, another solution as noticed standard output and error are still redirected to tty
exec 0<&1
The original question has the important part missing and it is my fault not making it very clear in very first place. It became apparent after #NahuelFouilleul's comment. The confirmation/question prompt was not waiting for user to hit a key. The reason was because my bash script was being called by a git hook. Things seem to be done in slightly different way in such cases. The solution is below but the original answer is here.
#!/bin/bash
exec < /dev/tty
while true; do
read -p "Accepting the offer? (y/n) " answer
if [[ $answer =~ ^[Yy]$ ]] ;
then
echo "Accepted"
else
echo "Not accepted"
fi
break
done
Try this:
echo -n "Continue (y/n)?"
read CONT
if [ "$CONT" = "n" ]
then
echo "NO"
else
echo "YES"
fi
the echo -n means no newline
Related
I have a script and want to ask the user for some information, but the script cannot continue until the user fills in this information. The following is my attempt at putting a command into a loop to achieve this but it doesn't work for some reason:
echo "Please change password"
while passwd
do
echo "Try again"
done
I have tried many variations of the while loop:
while `passwd`
while [[ "`passwd`" -gt 0 ]]
while [ `passwd` -ne 0 ]]
# ... And much more
But I can't seem to get it to work.
until passwd
do
echo "Try again"
done
or
while ! passwd
do
echo "Try again"
done
To elaborate on #Marc B's answer,
$ passwd
$ while [ $? -ne 0 ]; do !!; done
Is nice way of doing the same thing that's not command specific.
You need to test $? instead, which is the exit status of the previous command. passwd exits with 0 if everything worked ok, and non-zero if the passwd change failed (wrong password, password mismatch, etc...)
passwd
while [ $? -ne 0 ]; do
passwd
done
With your backtick version, you're comparing passwd's output, which would be stuff like Enter password and confirm password and the like.
If anyone looking to have retry limit:
max_retry=5
counter=0
until $command
do
sleep 1
[[ counter -eq $max_retry ]] && echo "Failed!" && exit 1
echo "Trying again. Try #$counter"
((counter++))
done
You can use an infinite loop to achieve this:
while true
do
read -p "Enter password" passwd
case "$passwd" in
<some good condition> ) break;;
esac
done
while [ -n $(passwd) ]; do
echo "Try again";
done;
I have a script and want to ask the user for some information, but the script cannot continue until the user fills in this information. The following is my attempt at putting a command into a loop to achieve this but it doesn't work for some reason:
echo "Please change password"
while passwd
do
echo "Try again"
done
I have tried many variations of the while loop:
while `passwd`
while [[ "`passwd`" -gt 0 ]]
while [ `passwd` -ne 0 ]]
# ... And much more
But I can't seem to get it to work.
until passwd
do
echo "Try again"
done
or
while ! passwd
do
echo "Try again"
done
To elaborate on #Marc B's answer,
$ passwd
$ while [ $? -ne 0 ]; do !!; done
Is nice way of doing the same thing that's not command specific.
You need to test $? instead, which is the exit status of the previous command. passwd exits with 0 if everything worked ok, and non-zero if the passwd change failed (wrong password, password mismatch, etc...)
passwd
while [ $? -ne 0 ]; do
passwd
done
With your backtick version, you're comparing passwd's output, which would be stuff like Enter password and confirm password and the like.
If anyone looking to have retry limit:
max_retry=5
counter=0
until $command
do
sleep 1
[[ counter -eq $max_retry ]] && echo "Failed!" && exit 1
echo "Trying again. Try #$counter"
((counter++))
done
You can use an infinite loop to achieve this:
while true
do
read -p "Enter password" passwd
case "$passwd" in
<some good condition> ) break;;
esac
done
while [ -n $(passwd) ]; do
echo "Try again";
done;
I'm learning BASH scripting, and I've got a problem. I wrote a generator, that checks if there is script with same name as given and if not it makes one, makes it executable and gives it proper shebang. But it doesn't work. I wanted it to exit when there is already one script with that name. Can you tell me what I'm doing wrong?
#!/bin/bash
clear
echo "Name the script"
read name
echo "What shell? (bash/sh)"
read type
if [ -e ./$name ]
then
echo "You already have that script"
read
exit
else
touch $name
chmod 755 $name
fi
case $type in
"bash") echo '#!/bin/bash' > $name ;;
"sh") echo '#!/bin/bash' > $name ;;
*) echo "I don't know what do you want" ;;
esac
vim $name
There is an extra read which is holding you up:
if [[ -e ./$name ]]
then
echo "'./$name' already exists" # <<<< always report the name
# read <<<< This is the line which is causing problems
exit 1 # <<<< indicate there was an error
else
touch "$name"
chmod 755 "$name"
fi
Quote filenames incase they contain embedded whitespace (you don't need them around variables if you use [[ ]].
Also note that the #! line for sh is wrong (you are using bash for both)
Edit:
If you have /etc/shells on your system, this is a good exercise in using select menu. Try this:
#!/bin/bash
read -p "Name the script: " name
if [[ -e ./$name ]]
then
echo "'./$name' already exists" # <<<< always report the name
# read <<<< This is the line which is causing problems
exit 1 # <<<< indicate there was an error
else
touch "$name"
chmod 755 "$name"
fi
declare -a shells
i=0
while read shell
do
if [[ $shell != \#* && $shell != "" ]]
then
shells[i++]="$shell"
fi
done < /etc/shells
PS3='Please select the shell: '
select type in "${shells[#]}" QUIT
do
echo "You selected $type"
if [[ $type == QUIT ]]
then
exit 2
fi
break
done
echo "#!$type" > "$name"
vim $name
You might also consider using $EDITOR rather than hard-coding vim.
This question already has answers here:
How do I prompt for Yes/No/Cancel input in a Linux shell script?
(37 answers)
Closed 28 days ago.
How do I ask a yes/no type question in Bash?
I ask the question... echo "Do you like pie?"
And receive the answer... read pie
How do I do something if the answer is yes, or starts with y (so yes and yeah, etc, will work too).
I like to use the following function:
function yes_or_no {
while true; do
read -p "$* [y/n]: " yn
case $yn in
[Yy]*) return 0 ;;
[Nn]*) echo "Aborted" ; return 1 ;;
esac
done
}
So in your script you can use like this:
yes_or_no "$message" && do_something
In case the user presses any key other than [yYnN] it will repeat the message.
This works too:
read -e -p "Do you like pie? " choice
[[ "$choice" == [Yy]* ]] && echo "doing something" || echo "that was a no"
Pattern starting with Y or y will be taken as yes.
I like Jahid's oneliner. Here is a slight simplification of it:
[[ "$(read -e -p 'Continue? [y/N]> '; echo $REPLY)" == [Yy]* ]]
Here are some tests:
$ [[ "$(read -e -p 'Continue? [y/N]> '; echo $REPLY)" == [Yy]* ]] && echo Continuing || echo Stopping
Continue? [y/N]> yes
Continuing
$ for test_string in y Y yes YES no ''; do echo "Test String: '$test_string'"; echo $test_string | [[ "$(read -e -p 'Continue? [y/N]>'; echo $REPLY)" == [Yy]* ]] && echo Continuing || echo Stopping; done
Test String: 'y'
Continuing
Test String: 'Y'
Continuing
Test String: 'yes'
Continuing
Test String: 'YES'
Continuing
Test String: 'no'
Stopping
Test String: ''
Stopping
Update
In response to a comment, I'm going to add an adaptation to make this work in zsh.
Disclaimer
I would never write a shell script in zsh even though it is now my primary interactive shell. I still write all scripts in bash or sh. However, since you sometimes need to script modifications to your interactive shell (ex: source ~/dev/set_env), you might want to include prompting.
#! /usr/bin/env zsh
[[ "$(echo -n 'Continue? [y/N]> ' >&2; read; echo $REPLY)" == [Yy]* ]] \
&& echo Continuing \
|| echo Stopping
This works:
echo "Do you like pie?"
read pie
if [[ $pie == y* ]]; then
echo "You do! Awesome."
else
echo "I don't like it much, either."
fi
[[ $pie == y* ]] tests to see of the variable $pie starts with y.
Feel free to make this better if you'd like.
In contrast to the other answers this function gives you the possibility to set a default:
function askYesNo {
QUESTION=$1
DEFAULT=$2
if [ "$DEFAULT" = true ]; then
OPTIONS="[Y/n]"
DEFAULT="y"
else
OPTIONS="[y/N]"
DEFAULT="n"
fi
read -p "$QUESTION $OPTIONS " -n 1 -s -r INPUT
INPUT=${INPUT:-${DEFAULT}}
echo ${INPUT}
if [[ "$INPUT" =~ ^[yY]$ ]]; then
ANSWER=true
else
ANSWER=false
fi
}
askYesNo "Do it?" true
DOIT=$ANSWER
if [ "$DOIT" = true ]; then
< do some stuff >
fi
On the command line you would see
Do it? [Y/n] y
Here is a short function:
prompt(){ read -p "$1" a; return $(test $a = "y"); }
Usage (if the answer is y then do_something executed):
prompt "Do you want it?" && do_something
Usage with multiple commands:
prompt "Do you want it?" && {
do_something1
do_something2
}
I have a script that checks if a file exists or not using the ls command. If there is not a file I ask the user if he would like to continue with the script.
What I am finding is that the read command excepts input from the terminal instead of the keyboard?
Here is my script:
function isfileThere(){
output=$(ls ${1} 2>&1 >/dev/null)
case $output in
*"No such file or directory"*)
echo "DS not found: $output";
option_exitprog; $output >> DSNotFound.txt ;;
*) echo "DS found: $output";;
esac
}
function option_exitprog(){
while :
do
echo -n "Would you like to continue (y/n)?"
read Answer
#read -n1 -p "Would you like to continue (y/n)?" Answer
if [ ! -z "$Answer" ] ; then
if [ "$Answer" == "y" ] ; then
echo "Exiting script. Goodbye"
exit 1
elif [ "$Answer" == "n" ] ; then
echo "Continue With Program"
break
else
echo "We only accept (y/n)"
fi
else
echo "You have entered a null string. We only accept (y/n)"
fi
done
}
function get_TotalEventEntries(){
cat<<EOF
####################################
# #
# #
# get Total Entries #
# #
# #
####################################
EOF
while read LINE
do
let total_DSNumber=total_DSNumber+1
#Check if files exist
isfileThere ${FileDir}/*${LINE}*/*.root*
#print to file
#printf "${LINE}=" >> ${Filename}
#getEntries ${LINE} >> ${Filename}
done < ${DSWildCardFile}
echo "Finished running over $total_DSNumber file(s)"
}
get_TotalEventEntries
The problem is at this line: done < ${DSWildCardFile}. You cannot read lines from this file and read user at the same time with read and simple redirection . To fix it, use more complex redirection and a new file descriptor:
while read -u 3 LINE
do
...
done 3< ${DSWildCardFile}