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

  • posix compliant: could work on poor systems with generic shell environments
  • bash specific: using so called bashisms
  • and if you want

  • simple ``in line'' question / answer (generic solutions)
  • pretty formatted interfaces, like ncurses 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
    

    # (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”是什么意思?

    下一篇: 如何在Linux shell脚本中提示是/否/取消输入?