Edit 2015-10-29: Adding readline's history capacity Depending on posix compliant: could work on poor system with generic shell environments bash specific: using so called bashisms and if you want simple in line question / answer (generic solutions) pretty formated interfaces, like ncurses or more graphical using libgtk or libqt... use powerful readline history capability (new oct 2015)
echo -n "Is this a good question (y/n)? "
read answer
if echo "$answer" | grep -iq "^y" ;then
echo Yes
else
echo No
fi
Posix, but single key feature
(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 # Care 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
There is a bunch of tools which were built, using 'libncurses, libgtk, libqt' or any graphical library, for this kind of goal:
if whiptail --yesno "Is this a good question" 20 60 ;then
echo Yes
else
echo No
fi
Depending on which distribution you're using, you could replace whiptail by:
$ 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 dialog box.
Of course this values does not matter for graphical interfaces like gdialog and kdialog, but all these tools have to use the same syntax.
DIALOG=whiptail
if [ -x /usr/bin/dialog ] ;then DIALOG=gdialog ; fi
if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
...
$DIALOG --yesno ...
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 - if needed... - in line with single key feature
For this, under bash, we just have to specify the length of attended input for read command is 1
read -n 1 -p "Is this a good question (y/n)? " answer
Under bash, read command accept 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
Of course, all graphical tools work same under bash:
if whiptail --yesno "Is this a good question" 20 60 ;then
echo Yes
else
echo No
fi
- If using dialog seems easy to use for simple yes/no purpose, using them for a more sophisticated dialog box may be hard to use:
whiptail --menu "Is this a good question" 20 60 12 y Yes n No m Maybe
- Storing answer into a variable is sometimes tricky:
The standard output is for interface drawing and the answer is printed on the error output:
answer=$($DIALOG --menu "Is this a good question" \
20 60 12 y Yes n No m Maybe 2>&1 >/dev/tty)
or under bash:
read answer < <($DIALOG 2>&1 >/dev/tty --menu \
"Is this a good question" 20 60 12 y Yes n No m Maybe)
or
read answer < <($DIALOG 2>&1 >/dev/tty --passwordbox "Enter pass" 20 60)
echo "Your pass is: $answer"
- Progress bar:
$DIALOG --gauge "Filling the tank" 20 60 0 < <(
for i in {1..100};do
printf "XXX\n%d\n%(%a %b %T)T progress: %d\nXXX\n" $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 "XXX\n%d\n%%a %%b %%T progress: %d\nXXX\n" $i $i`"
sleep .0125
done | $DIALOG --gauge "Filling the tank" 20 60 0
$DIALOG --infobox "This is a simple info box\n\nNo 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: $AnsYesNo\nInput: $AnsInput\nSecret: $AnsPass"
$DIALOG --msgbox "$out\nAttribs: $AnsCkLst\nNote: $AnsRadio" 20 60
done
- Using readline's history
More than words, look (or try) this sample:
#!/bin/sh
set -i
sub myread() {
HISTFILE=~/.myscript.history
history -c
history -r $HISTFILE
read -e -p '> ' $1
}
while myread line;do
[ "$line" = "exit" ] && break
echo >>$HISTFILE "$line"
echo "Doing something with '$line'"
done
This will create a file .myscript.history in your $HOME directory.
Then you could use readline's history commands, like Up, Down, Ctrl+r and others.