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

我想在shell脚本中暂停输入,并提示用户进行选择。 标准的“是,否,或取消”类型问题。 我如何在典型的bash提示符下完成此操作?


在shell提示下获取用户输入的最简单和最广泛的方法是read命令。 说明其用途的最佳方式是一个简单的演示:

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

另一种由Steven Huwig指出的方法是Bash的select命令。 这里是使用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

select您不需要清理输入 - 它会显示可用的选项,然后键入与您的选择相对应的编号。 它也会自动循环,所以如果给出无效输入,就不需要一段while true循环来重试。

另外,请查看F. Hauri的优秀答案。


一个通用问题至少有五个答案。

取决于

  • 符合posix:可以在通用shell环境下运行在较差的系统上
  • bash具体:使用所谓的bashisms
  • 如果你想

  • 简单``在线''问题/答案(通用解决方案)
  • 相当格式化的接口,如ncurses或更多图形使用libgtk或libqt ...
  • 使用强大的readline历史记录功能
  • 1. POSIX通用解决方案

    你可以使用read命令,接下来是if ... then ... else

    echo -n "Is this a good question (y/n)? "
    read answer
    

    # if echo "$answer" | grep -iq "^y" ;then
    

    #(感谢Adam Katz的评论:这个测试更便携,避免一个分叉:)

    if [ "$answer" != "${answer#[Yy]}" ] ;then
        echo Yes
    else
        echo No
    fi
    

    POSIX,但单一的关键功能

    但是如果你不希望用户必须点击返回,你可以这样写:

    编辑:正如@JonathanLeffler正确地表明,保存stty的配置可能比简单地强制他们理智。)

    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
    

    注意:这是在sh,bash,ksh,dash和busybox下测试的!

    相同,但是明确地等待y或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
    

    使用专用工具

    有许多工具是使用libncurseslibgtklibqt或其他图形库libqt 。 例如,使用whiptail

    if whiptail --yesno "Is this a good question" 20 60 ;then
        echo Yes
    else
        echo No
    fi
    

    根据您的系统,您可能需要用另一个类似的工具替换whiptail

    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
    

    其中20是对话框的行数,行数是60 ,对话框的宽度是60 。 这些工具都具有几乎相同的语法。

    DIALOG=whiptail
    if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi
    if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
    ...
    $DIALOG --yesno ...
    

    2.打击具体解决方案

    基本的在线方法

    read -p "Is this a good question (y/n)? " answer
    case ${answer:0:1} in
        y|Y )
            echo Yes
        ;;
        * )
            echo No
        ;;
    esac
    

    我更喜欢使用case所以我甚至可以测试yes | ja | si | oui yes | ja | si | oui yes | ja | si | oui如果需要...

    符合单一关键功能

    在bash下,我们可以为read命令指定预期输入的长度:

    read -n 1 -p "Is this a good question (y/n)? " answer
    

    在bash下, read命令接受一个超时参数,这可能很有用。

    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
    

    专用工具的一些技巧

    更复杂的对话框,超越简单yes - no目的:

    dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe
    

    进度条:

    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
    ) 
    

    小演示:

    #!/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.使用readline的历史

    例:

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

    这将在您的$HOME目录中创建一个.myscript.history文件,比您可以使用readline的历史命令(如Up,Down,Ctrl + r等)。


    echo "Please enter some input: "
    read input_variable
    echo "You entered: $input_variable"
    
    链接地址: http://www.djcxy.com/p/635.html

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

    下一篇: Calling shell commands from Ruby