How do I prompt for Yes/No/Cancel input in a Linux shell script? How do I prompt for Yes/No/Cancel input in a Linux shell script? shell shell

How do I prompt for Yes/No/Cancel input in a Linux shell script?


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.";;    esacdone

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;;    esacdone

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, Léa Gris demonstrated a way to make the request language agnostic in her answer. Adapting my first example to better serve multiple languages might look like this:

set -- $(locale LC_MESSAGES)yesptrn="$1"; noptrn="$2"; yesword="$3"; noword="$4"while true; do    read -p "Install (${yesword} / ${noword})? " yn    if [[ "$yn" =~ $yesexpr ]]; then make install; exit; fi    if [[ "$yn" =~ $noexpr ]]; then exit; fi    echo "Answer ${yesword} / ${noword}."done

Obviously other communication strings remain untranslated here (Install, Answer) which would need to be addressed in a more fully completed translation, but even a partial translation would be helpful in many cases.

Finally, please check out the excellent answer by F. Hauri.


At least five answers for one generic question.

Depending on

  • compliant: could work on poor systems with generic environments
  • specific: using so called bashisms

and if you want

  • simple ``in line'' question / answer (generic solutions)
  • pretty formatted interfaces, like or more graphical using libgtk or libqt...
  • use powerful readline history capability

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

if [ "$answer" != "${answer#[Yy]}" ] ;then    echo Yeselse    echo Nofi

(Thanks to Adam Katz's comment: Replaced the test above with one that is more portable and avoids one fork:)

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 sttyif echo "$answer" | grep -iq "^y" ;then    echo Yeselse    echo Nofi

Note: This was tested under , , , and !

Same, but waiting explicitly for y or n:

#/bin/shecho -n "Is this a good question (y/n)? "old_stty_cfg=$(stty -g)stty raw -echoanswer=$( while ! head -c 1 | grep -i '[ny]' ;do true ;done )stty $old_stty_cfgif echo "$answer" | grep -iq "^y" ;then    echo Yeselse    echo Nofi

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 Yeselse    echo Nofi

Depending on your system, you may need to replace whiptail with another similiar tool:

dialog --yesno "Is this a good question" 20 60 && echo Yesgdialog --yesno "Is this a good question" 20 60 && echo Yeskdialog --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=whiptailif [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fiif [ -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)? " answercase ${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

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

3. 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 "XXX\n%d\n%(%a %b %T)T progress: %d\nXXX\n" $i -1 $i        sleep .033    done) 

Little demo:

#!/bin/shwhile 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

More sample? Have a look at Using whiptail for choosing USB device and USB removable storage selector: USBKeyChooser

5. Using readline's history

Example:

#!/bin/bashset -iHISTFILE=~/.myscript.historyhistory -chistory -rmyread() {    read -e -p '> ' $1    history -s ${!1}}trap 'history -a;exit' 0 1 2 3 6while 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_variableecho "You entered: $input_variable"