How do I prompt for Yes/No/Cancel input in a Linux shell script?
I want to pause input in a shell script, and prompt the user for choices. The standard 'Yes, No, or Cancel' type question. How do I accomplish this in a typical bash prompt?
The simplest and most widely available method to get user input at a shell prompt is the read
command. The best way to illustrate its use is a simple demonstration:
while true; do
read -p "Do you wish to install this program?" yn
case $yn in
[Yy]* ) make install; break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done
Another method, pointed out by Steven Huwig, is Bash's select
command. Here is the same example using select
:
echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
case $yn in
Yes ) make install; break;;
No ) exit;;
esac
done
With select
you don't need to sanitize the input – it displays the available choices, and you type a number corresponding to your choice. It also loops automatically, so there's no need for a while true
loop to retry if they give invalid input.
Also, please check out the excellent answer by F. Hauri.
At least five answers for one generic question.
Depending on
and if you want
1. POSIX generic solutions
You could use the read
command, followed by if ... then ... else
:
echo -n "Is this a good question (y/n)? "
read answer
# if echo "$answer" | grep -iq "^y" ;then
# (Thanks to Adam Katz's comment: This test is more portable and avoid one fork:)
if [ "$answer" != "${answer#[Yy]}" ] ;then
echo Yes
else
echo No
fi
POSIX, but single key feature
But if you don't want the user to have to hit Return, you could write:
( Edited: As @JonathanLeffler rightly suggest, saving stty's configuration could be better than simply force them to sane.)
echo -n "Is this a good question (y/n)? "
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg # Careful playing with stty
if echo "$answer" | grep -iq "^y" ;then
echo Yes
else
echo No
fi
Note: This was tested under sh, bash, ksh, dash and busybox!
Same, but waiting explicitly for y or n:
#/bin/sh
echo -n "Is this a good question (y/n)? "
old_stty_cfg=$(stty -g)
stty raw -echo
answer=$( while ! head -c 1 | grep -i '[ny]' ;do true ;done )
stty $old_stty_cfg
if echo "$answer" | grep -iq "^y" ;then
echo Yes
else
echo No
fi
Using dedicated tools
There are many tools which were built using libncurses
, libgtk
, libqt
or other graphical libraries. For example, using whiptail
:
if whiptail --yesno "Is this a good question" 20 60 ;then
echo Yes
else
echo No
fi
Depending on your system, you may need to replace whiptail
with another similiar tool:
dialog --yesno "Is this a good question" 20 60 && echo Yes
gdialog --yesno "Is this a good question" 20 60 && echo Yes
kdialog --yesno "Is this a good question" 20 60 && echo Yes
where 20
is height of dialog box in number of lines and 60
is width of the dialog box. These tools all have near same syntax.
DIALOG=whiptail
if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi
if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
...
$DIALOG --yesno ...
2. Bash specific solutions
Basic in line method
read -p "Is this a good question (y/n)? " answer
case ${answer:0:1} in
y|Y )
echo Yes
;;
* )
echo No
;;
esac
I prefer to use case
so I could even test for yes | ja | si | oui
yes | ja | si | oui
yes | ja | si | oui
if needed...
in line with single key feature
Under bash, we can specify the length of intended input for for the read
command:
read -n 1 -p "Is this a good question (y/n)? " answer
Under bash, read
command accepts a timeout parameter, which could be useful.
read -t 3 -n 1 -p "Is this a good question (y/n)? " answer
[ -z "$answer" ] && answer="Yes" # if 'yes' have to be default choice
Some tricks for dedicated tools
More sophisticated dialog boxes, beyond simple yes - no
purposes:
dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe
Progress bar:
dialog --gauge "Filling the tank" 20 60 0 < <(
for i in {1..100};do
printf "XXXn%dn%(%a %b %T)T progress: %dnXXXn" $i -1 $i
sleep .033
done
)
Little demo:
#!/bin/sh
while true ;do
[ -x "$(which ${DIALOG%% *})" ] || DIALOG=dialog
DIALOG=$($DIALOG --menu "Which tool for next run?" 20 60 12 2>&1
whiptail "dialog boxes from shell scripts" >/dev/tty
dialog "dialog boxes from shell with ncurses"
gdialog "dialog boxes from shell with Gtk"
kdialog "dialog boxes from shell with Kde" ) || exit
clear;echo "Choosed: $DIALOG."
for i in `seq 1 100`;do
date +"`printf "XXXn%dn%%a %%b %%T progress: %dnXXXn" $i $i`"
sleep .0125
done | $DIALOG --gauge "Filling the tank" 20 60 0
$DIALOG --infobox "This is a simple info boxnnNo action required" 20 60
sleep 3
if $DIALOG --yesno "Do you like this demo?" 20 60 ;then
AnsYesNo=Yes; else AnsYesNo=No; fi
AnsInput=$($DIALOG --inputbox "A text:" 20 60 "Text here..." 2>&1 >/dev/tty)
AnsPass=$($DIALOG --passwordbox "A secret:" 20 60 "First..." 2>&1 >/dev/tty)
$DIALOG --textbox /etc/motd 20 60
AnsCkLst=$($DIALOG --checklist "Check some..." 20 60 12
Correct "This demo is useful" off
Fun "This demo is nice" off
Strong "This demo is complex" on 2>&1 >/dev/tty)
AnsRadio=$($DIALOG --radiolist "I will:" 20 60 12
" -1" "Downgrade this answer" off
" 0" "Not do anything" on
" +1" "Upgrade this anser" off 2>&1 >/dev/tty)
out="Your answers:nLike: $AnsYesNonInput: $AnsInputnSecret: $AnsPass"
$DIALOG --msgbox "$outnAttribs: $AnsCkLstnNote: $AnsRadio" 20 60
done
5. Using readline's history
Example:
#!/bin/bash
set -i
HISTFILE=~/.myscript.history
history -c
history -r
myread() {
read -e -p '> ' $1
history -s ${!1}
}
trap 'history -a;exit' 0 1 2 3 6
while myread line;do
case ${line%% *} in
exit ) break ;;
* ) echo "Doing something with '$line'" ;;
esac
done
This will create a file .myscript.history
in your $HOME
directory, than you could use readline's history commands, like Up, Down, Ctrl+r and others.
echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"
链接地址: http://www.djcxy.com/p/636.html
上一篇: 在shell中,“2>&1”是什么意思?