【问题标题】:How do I prompt for Yes/No/Cancel input in a Linux shell script?如何在 Linux shell 脚本中提示是/否/取消输入?
【发布时间】:2026-01-07 11:20:02
【问题描述】:

我想暂停 shell 脚本中的输入,并提示用户选择。
标准的YesNoCancel 类型的问题。
如何在典型的 bash 提示符中完成此操作?

【问题讨论】:

  • 请注意:提示的约定是,如果您提供[yn] 选项,则大写的是默认值,即[Yn] 默认为“yes”,[yN]默认为“否”。见ux.stackexchange.com/a/40445/43532
  • 任何从ZSH来的人,如何使用read命令提示见this answer
  • 您也可以考虑my related Q/A on U&L.SE 来了解在bash 中暂停的规范方式。提供的结果可以很容易地转移。

标签: linux bash shell scripting


【解决方案1】:

单线:

read -p "Continue? [Enter] → Yes, [Ctrl]+[C] → No."

这假定“否”和“取消”具有相同的结果,因此没有理由区别对待它们。

【讨论】:

    【解决方案2】:

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

    取决于

    • 兼容:可以在具有通用 环境的较差系统上工作
    • 具体:使用所谓的 bashisms

    如果你愿意

    • 简单的“在线”问题/答案(通用解决方案)
    • 漂亮的格式化界面,例如 或更多使用 libgtk 或 libqt 的图形...
    • 使用强大的读取行历史记录功能

    1。 POSIX 通用解决方案

    您可以使用read 命令,然后使用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 # this grammar (the #[] operator) means that the variable $answer where any Y or y in 1st position will be dropped if they exist.
        echo Yes
    else
        echo No
    fi
    

    (感谢Adam Katz's comment:将上面的测试替换为更便携且避免一个fork的测试:)

    POSIX,但单键功能

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

    已编辑:正如@JonathanLeffler 正确建议的那样,保存 stty 的配置可能比简单地强制他们理智更好。) p>

    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
    

    注意:这是在下测试的!

    相同,但明确等待 yn

    #/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 或其他图形库构建的。例如,使用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 是对话框的宽度。这些工具都有几乎相同的语法。

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

    2。 Bash 特定解决方案

    基本行内方法

    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...

    符合单键功能

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

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

    在 bash 下,read 命令接受 timeout 参数,这可能很有用。

    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。 专用工具的一些技巧

    更复杂的对话框,超越简单的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 "XXX\n%d\n%(%a %b %T)T progress: %d\nXXX\n" $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 "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 whiptail for choosing USB deviceUSB removable storage selector: USBKeyChooser

    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 的历史命令,例如 UpDownCtrl+r 等。

    【讨论】:

    • 请注意,stty 提供了-g 使用选项:old_stty=$(stty -g); stty raw -echo; …; stty "$old_stty"。这会将设置完全还原为找到时的设置,可能与 stty -sane 相同,也可能不同。
    • read answer 将在空格和换行符之前解释反斜杠,否则会剥离它们,这是很少打算的。使用read -r answer 代替SC2162
    • “使用 readline 的历史记录”方法非常不适合 OP 的问题。我很高兴你把它包括在内。我有几十个更复杂的脚本,我打算用这种模式更新!
    • 您可以将case 用于 POSIX 以及 bash(使用通配符条件而不是 bash 子字符串:case $answer in; [Yy]* ) echo Yes ;;),但我更喜欢使用条件语句,而不是 [ "$answer" != "${answer#[Yy]}" ]你的echo "$answer" | grep -iq ^y。它更便携(一些非 GNU grep 不能正确实现 -q)并且它没有系统调用。 ${answer#[Yy]} 使用参数扩展从$answer 的开头删除Yy,当其中任何一个存在时会导致不等式。这适用于任何 POSIX shell(dash、ksh、bash、zsh、busybox 等)。
    • @CarterPape 是的,这是个笑话!但是在这个详细的答案中,您可能会发现很多提示(第二点至少存在3种不同的方法)!而且……从现在起大约 5 年,你是第一个讲述我的计数方法的人! ;-))
    【解决方案3】:

    我为是/否问题制作了这个小脚本:https://github.com/optimistiCli/getans

    例子:

    #!/bin/bash
    
    if ! getans.sh 'Shall we proceed?' y ; then
        echo "User said “NO”"
        exit 1
    fi
    
    echo "User said “YES”"
    # do something usefull
    exit 0
    

    直接链接: https://github.com/optimistiCli/getans/raw/main/getans.sh

    【讨论】:

      【解决方案4】:

      在 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
      

      另一种方法,pointed out by 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 循环提供无效输入,则无需重试。

      此外,Léa Grisher answer 中演示了一种使请求语言不可知的方法。调整我的第一个示例以更好地服务于多种语言可能如下所示:

      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
      

      显然,其他通信字符串在此处仍未翻译(安装、回答),需要在更完整的翻译中解决这些问题,但在许多情况下,即使是部分翻译也会有所帮助。

      最后,请通过F. Hauri查看excellent answer

      【讨论】:

      • 在 OS X Leopard 中使用 Bash,我将 exit 更改为 break 以防止在选择“否”时关闭选项卡。
      • 对于长于“是”或“否”的选项,这如何工作?在 case 子句中,您是否编写了类似的内容:安装程序,之后什么也不做) make install;休息;
      • @Shawn 使用 read 当然,您可以使用 bash shell 的 switch 语句支持的任何 glob 或正则表达式模式。它只是将键入的文本与您的模式匹配,直到找到匹配项。使用select,选项列表被提供给命令并将它们显示给用户。您可以将列表中的项目设置为任意长或短。我建议检查他们的手册页以获取全面的使用信息。
      • 如果没有循环,为什么break 中有select
      • FWIW,我使用此示例创建了一个脚本,我打算通过远程 SSH 会话触发该脚本。我的 SSH 命令如下所示:ssh my-server 'path/to/myscript.sh'。以这种方式执行时,read -p 命令的提示文本不会出现。但是,echo 命令的输出确实如此。所以对我来说,更好的解决方案是使用echo -n "Do something? ",然后使用read yn
      【解决方案5】:

      检查一下

      read -p "Continue? (y/n): " confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1
      

      【讨论】:

        【解决方案6】:

        在我的情况下,我需要从下载的脚本中读取,即curl -Ss https://example.com/installer.sh | sh

        read yesno &lt; /dev/tty 成功了

        echo -n "These files will be uploaded. Is this ok? (y/n) "
        read yesno < /dev/tty
        
        if [ "x$yesno" = "xy" ];then
           
           # Yes
        else
        
           # No
        fi
        

        【讨论】:

        • 其中一个重要部分是输入验证。我认为调整我的第一个示例以像您一样接受tty 输入也会为您做同样的事情,并且还会因输入错误而循环(想象缓冲区中有几个字符;您的方法会迫使用户总是 选择否)。
        【解决方案7】:

        绝对最简单的解决方案是这种没有巧妙技巧的单线:

        read -p "press enter ..." y
        

        它让人想起经典的 DOS Hit any key to continue,只是它等待 Enter 键,而不仅仅是任何键。

        确实,这并没有为您提供三个选择 Yes No Cancel,但在您接受 control-C 作为 No resp 时它很有用。在简单的脚本中取消,例如:

        #!/bin/sh
        echo Backup this project
        read -p "press enter ..." y
        rsync -tavz . /media/hard_to_remember_path/backup/projects/yourproject/
        

        因为您不喜欢记住丑陋的命令和路径,但又不想记住运行速度过快的脚本,在您确定它不是您要运行的脚本之前没有给您机会停下来。

        【讨论】:

          【解决方案8】:

          这是我在脚本/函数中通常需要的:

          • 如果您按 ENTER,则默认答案是“是”
          • 也接受 z(如果你搞错了,你是在 QWERTZ 布局上)
          • 接受其他语言(“ja”、“Oui”、...)
          • 如果您在函数内部,请处理正确的退出
          while true; do
              read -p "Continue [Y/n]? " -n 1 -r -e yn
              case "${yn:-Y}" in
                  [YyZzOoJj]* ) echo; break ;;
                  [Nn]* ) [[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1 ;; # handle exits from shell or function but don't exit interactive shell
                  * ) echo "Please answer yes or no.";;
              esac
          done
          echo "and off we go!"
          

          【讨论】:

            【解决方案9】:

            可以在 POSIX shell 中处理可识别区域设置的“是/否选择”;通过使用 LC_MESSAGES 语言环境类别的条目,witch 提供现成的 RegEx 模式来匹配输入,以及本地化 Yes No 的字符串。

            #!/usr/bin/env sh
            
            # Getting LC_MESSAGES values into variables
            # shellcheck disable=SC2046 # Intended IFS splitting
            IFS='
            ' set -- $(locale LC_MESSAGES)
            
            yesexpr="$1"
            noexpr="$2"
            yesstr="$3"
            nostr="$4"
            messages_codeset="$5" # unused here, but kept as documentation
            
            # Display Yes / No ? prompt into locale
            echo "$yesstr / $nostr ?"
            
            # Read answer
            read -r yn
            
            # Test answer
            case "$yn" in
            # match only work with the character class from the expression
              ${yesexpr##^}) echo "answer $yesstr" ;;
              ${noexpr##^}) echo "answer $nostr" ;;
            esac
            

            编辑: 正如his comment中提到的@Urhixidur

            不幸的是,POSIX 只指定了前两个(yesexpr 和 noexpr)。在 Ubuntu 16 上,yesstr 和 nostr 为空。

            见:https://www.ee.ryerson.ca/~courses/ele709/susv4/xrat/V4_xbd_chap07.html#tag_21_07_03_06

            LC_MESSAGES

            yesstrnostr 语言环境关键字以及 YESSTRNOSTR langinfo 项以前用于匹配用户的肯定和否定响应。在 POSIX.1-2008 中,yesexprnoexprYESEXPRNOEXPR 扩展正则表达式已取代它们。应用程序应使用通用的基于语言环境的消息传递工具来发出提示消息,其中包括示例所需的响应。

            或者使用 Bash 方式使用语言环境:

            #!/usr/bin/env bash
            
            IFS=$'\n' read -r -d '' yesexpr noexpr _ < <(locale LC_MESSAGES)
            
            printf -v yes_or_no_regex "(%s)|(%s)" "$yesexpr" "$noexpr"
            
            printf -v prompt $"Please answer Yes (%s) or No (%s): " "$yesexpr" "$noexpr"
            
            declare -- answer=;
            
            until [[ "$answer" =~ $yes_or_no_regex ]]; do
              read -rp "$prompt" answer
            done
            
            if [[ -n "${BASH_REMATCH[1]}" ]]; then
              echo $"You answered: Yes"
            else
              echo $"No, was your answer."
            fi
            

            使用语言环境提供的正则表达式匹配答案。

            要翻译剩余的消息,请使用bash --dump-po-strings scriptname 输出用于本地化的 po 字符串:

            #: scriptname:8
            msgid "Please answer Yes (%s) or No (%s): "
            msgstr ""
            #: scriptname:17
            msgid "You answered: Yes"
            msgstr ""
            #: scriptname:19
            msgid "No, was your answer."
            msgstr ""
            

            【讨论】:

            • 我喜欢添加与语言无关的选项。干得好。
            • 不幸的是,POSIX 只指定了前两个(yesexpr 和 noexpr)。在 Ubuntu 16 上,yesstr 和 nostr 为空。
            • 但是等等!还有更坏的消息! bash case 语句表达式不是常规的,它们是文件名表达式。因此 Ubuntu 16 的 yesexpr 和 noexpr(分别为“^[yY].*”和“^[nN].*”)将由于嵌入期间而完全失败。在正则表达式中,“.*”表示“任何非换行符,零次或多次”。但在 case 语句中,它是一个文字“。”后跟任意数量的字符。
            • 最后,在 shell 环境中使用 yesexprnoexpr 可以做到的最好的事情是在 Bash 的特定 RegEx 匹配 if [[ "$yn" =~ $yesexpr ]]; then echo $"Answered yes"; else echo $"Answered no"; fi 中使用它
            【解决方案10】:

            要获得类似 ncurses 的漂亮输入框,请使用命令 dialog,如下所示:

            #!/bin/bash
            if (dialog --title "Message" --yesno "Want to do something risky?" 6 25)
            # message box will have the size 25x6 characters
            then 
                echo "Let's do something risky"
                # do something risky
            else 
                echo "Let's stay boring"
            fi
            

            至少在 SUSE Linux 中默认安装对话框包。好像:

            【讨论】:

            • 还有一个--defaultno 参数来确保默认选择“否”选项。
            【解决方案11】:

            您可以使用内置的read 命令;使用-p 选项向用户提示问题。

            从 BASH4 开始,您现在可以使用 -i 提出答案:

            read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
            echo $FILEPATH
            

            (但请记住使用“readline”选项-e 允许使用箭头键编辑行)

            如果你想要一个“是/否”的逻辑,你可以这样做:

            read -e -p "
            List the content of your home dir ? [Y/n] " YN
            
            [[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/
            

            【讨论】:

            • 需要注意的是FILEPATH是你选择的变量名,是用命令提示符的答案设置的。因此,如果您随后运行 vlc "$FILEPATH",例如,vlc 将打开该文件。
            • 第二个例子中-e有什么好处(简单的是/否)?
            • 有什么理由使用-e -p 而不是-ep
            • 如果没有-e 标志/选项,您可能(取决于实现)无法输入“y”,然后改变主意并用“n”(或任何其他内容)替换它其他的);在记录命令时,为了可读性/清晰性等原因,单独列出选项会更好。
            • 提示:注意真正使用bash,而不是sh,就像this answer on U&L.SE中描述的那样。
            【解决方案12】:

            您可以在read 上使用默认的REPLY,将其转换为小写,然后通过表达式与一组变量进行比较。
            该脚本还支持ja/si/oui

            read -rp "Do you want a demo? [y/n/c] "
            
            [[ ${REPLY,,} =~ ^(c|cancel)$ ]] && { echo "Selected Cancel"; exit 1; }
            
            if [[ ${REPLY,,} =~ ^(y|yes|j|ja|s|si|o|oui)$ ]]; then
               echo "Positive"
            fi
            

            【讨论】:

              【解决方案13】:

              仅单次按键

              这是一种更长但可重复使用的模块化方法:

              • 返回0=yes 和1=no
              • 无需按 Enter - 只需一个字符
              • 可以按回车接受默认选择
              • 可以禁用默认选择以强制选择
              • 适用于 zshbash

              按回车时默认为“否”

              注意N 是大写的。这里输入被按下,接受默认值:

              $ confirm "Show dangerous command" && echo "rm *"
              Show dangerous command [y/N]?
              

              另请注意,[y/N]? 是自动附加的。 接受默认的“否”,因此不会回显任何内容。

              重新提示,直到给出有效响应:

              $ confirm "Show dangerous command" && echo "rm *"
              Show dangerous command [y/N]? X
              Show dangerous command [y/N]? y
              rm *
              

              按回车时默认为“是”

              注意Y 是大写的:

              $ confirm_yes "Show dangerous command" && echo "rm *"
              Show dangerous command [Y/n]?
              rm *
              

              上面,我只是按了回车,所以命令运行了。

              enter 没有默认值 - 需要 yn

              $ get_yes_keypress "Here you cannot press enter. Do you like this [y/n]? "
              Here you cannot press enter. Do you like this [y/n]? k
              Here you cannot press enter. Do you like this [y/n]?
              Here you cannot press enter. Do you like this [y/n]? n
              $ echo $?
              1
              

              这里,1 或 false 被返回。请注意,使用此较低级别的函数,您需要提供自己的 [y/n]? 提示符。

              代码

              # Read a single char from /dev/tty, prompting with "$*"
              # Note: pressing enter will return a null string. Perhaps a version terminated with X and then remove it in caller?
              # See https://unix.stackexchange.com/a/367880/143394 for dealing with multi-byte, etc.
              function get_keypress {
                local REPLY IFS=
                >/dev/tty printf '%s' "$*"
                [[ $ZSH_VERSION ]] && read -rk1  # Use -u0 to read from STDIN
                # See https://unix.stackexchange.com/q/383197/143394 regarding '\n' -> ''
                [[ $BASH_VERSION ]] && </dev/tty read -rn1
                printf '%s' "$REPLY"
              }
              
              # Get a y/n from the user, return yes=0, no=1 enter=$2
              # Prompt using $1.
              # If set, return $2 on pressing enter, useful for cancel or defualting
              function get_yes_keypress {
                local prompt="${1:-Are you sure [y/n]? }"
                local enter_return=$2
                local REPLY
                # [[ ! $prompt ]] && prompt="[y/n]? "
                while REPLY=$(get_keypress "$prompt"); do
                  [[ $REPLY ]] && printf '\n' # $REPLY blank if user presses enter
                  case "$REPLY" in
                    Y|y)  return 0;;
                    N|n)  return 1;;
                    '')   [[ $enter_return ]] && return "$enter_return"
                  esac
                done
              }
              
              # Credit: http://unix.stackexchange.com/a/14444/143394
              # Prompt to confirm, defaulting to NO on <enter>
              # Usage: confirm "Dangerous. Are you sure?" && rm *
              function confirm {
                local prompt="${*:-Are you sure} [y/N]? "
                get_yes_keypress "$prompt" 1
              }    
              
              # Prompt to confirm, defaulting to YES on <enter>
              function confirm_yes {
                local prompt="${*:-Are you sure} [Y/n]? "
                get_yes_keypress "$prompt" 0
              }
              

              【讨论】:

              • 当我测试这个脚本时,我得到的不是上面的输出,Show dangerous command [y/N]? [y/n]?Show dangerous command [Y/n]? [y/n]?
              • 谢谢@IliasKarim,我刚刚解决了这个问题。
              【解决方案14】:

              回应他人:

              您不需要在 BASH4 中指定大小写,只需使用 ',,' 将 var 设为小写即可。此外,我非常不喜欢将代码放在读取块内,获取结果并在读取块 IMO 之外处理它。还包括退出 IMO 的“q”。最后,为什么键入“是”只需使用 -n1 并按 y。

              示例:用户可以按 y/n 也可以按 q 退出。

              ans=''
              while true; do
                  read -p "So is MikeQ the greatest or what (y/n/q) ?" -n1 ans
                  case ${ans,,} in
                      y|n|q) break;;
                      *) echo "Answer y for yes / n for no  or q for quit.";;
                  esac
              done
              
              echo -e "\nAnswer = $ans"
              
              if [[ "${ans,,}" == "q" ]] ; then
                      echo "OK Quitting, we will assume that he is"
                      exit 0
              fi
              
              if [[ "${ans,,}" == "y" ]] ; then
                      echo "MikeQ is the greatest!!"
              else
                      echo "No? MikeQ is not the greatest?"
              fi
              

              【讨论】:

                【解决方案15】:

                是/否/取消

                功能

                #!/usr/bin/env bash
                @confirm() {
                  local message="$*"
                  local result=''
                
                  echo -n "> $message (Yes/No/Cancel) " >&2
                
                  while [ -z "$result" ] ; do
                    read -s -n 1 choice
                    case "$choice" in
                      y|Y ) result='Y' ;;
                      n|N ) result='N' ;;
                      c|C ) result='C' ;;
                    esac
                  done
                
                  echo $result
                }
                

                用法

                case $(@confirm 'Confirm?') in
                  Y ) echo "Yes" ;;
                  N ) echo "No" ;;
                  C ) echo "Cancel" ;;
                esac
                

                用干净的用户输入确认

                功能

                #!/usr/bin/env bash
                @confirm() {
                  local message="$*"
                  local result=3
                
                  echo -n "> $message (y/n) " >&2
                
                  while [[ $result -gt 1 ]] ; do
                    read -s -n 1 choice
                    case "$choice" in
                      y|Y ) result=0 ;;
                      n|N ) result=1 ;;
                    esac
                  done
                
                  return $result
                }
                

                用法

                if @confirm 'Confirm?' ; then
                  echo "Yes"
                else
                  echo "No"
                fi
                

                【讨论】:

                  【解决方案16】:

                  你想要:

                  • Bash 内置命令(即便携)
                  • 检查 TTY
                  • 默认答案
                  • 超时
                  • 有色问题

                  片段

                  do_xxxx=y                      # In batch mode => Default is Yes
                  [[ -t 0 ]] &&                  # If TTY => Prompt the question
                  read -n 1 -p $'\e[1;32m
                  Do xxxx? (Y/n)\e[0m ' do_xxxx  # Store the answer in $do_xxxx
                  if [[ $do_xxxx =~ ^(y|Y|)$ ]]  # Do if 'y' or 'Y' or empty
                  then
                      xxxx
                  fi
                  

                  说明

                  • [[ -t 0 ]] &amp;&amp; read ... => 如果是 TTY,则调用命令 read
                  • read -n 1 => 等待一个字符
                  • $'\e[1;32m ... \e[0m ' => 以绿色打印
                    (绿色很好,因为在白色/黑色背景下都可读)
                  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash 正则表达式

                  超时 => 默认答案为否

                  do_xxxx=y
                  [[ -t 0 ]] && {                   # Timeout 5 seconds (read -t 5)
                  read -t 5 -n 1 -p $'\e[1;32m
                  Do xxxx? (Y/n)\e[0m ' do_xxxx ||  # read 'fails' on timeout
                  do_xxxx=n ; }                     # Timeout => answer No
                  if [[ $do_xxxx =~ ^(y|Y|)$ ]]
                  then
                      xxxx
                  fi
                  

                  【讨论】:

                    【解决方案17】:

                    使用read 命令:

                    echo Would you like to install? "(Y or N)"
                    
                    read x
                    
                    # now check if $x is "y"
                    if [ "$x" = "y" ]; then
                        # do something here!
                    fi
                    

                    然后是你需要的所有其他东西

                    【讨论】:

                      【解决方案18】:

                      用最少的行数实现这一点的最简单方法如下:

                      read -p "<Your Friendly Message here> : y/n/cancel" CONDITION;
                      
                      if [ "$CONDITION" == "y" ]; then
                         # do something here!
                      fi
                      

                      if 只是一个示例:如何处理此变量取决于您。

                      【讨论】:

                        【解决方案19】:

                        我注意到没有人针对如此简单的用户输入发布显示多行回显菜单的答案,所以这是我的尝试:

                        #!/bin/bash
                        
                        function ask_user() {    
                        
                        echo -e "
                        #~~~~~~~~~~~~#
                        | 1.) Yes    |
                        | 2.) No     |
                        | 3.) Quit   |
                        #~~~~~~~~~~~~#\n"
                        
                        read -e -p "Select 1: " choice
                        
                        if [ "$choice" == "1" ]; then
                        
                            do_something
                        
                        elif [ "$choice" == "2" ]; then
                        
                            do_something_else
                        
                        elif [ "$choice" == "3" ]; then
                        
                            clear && exit 0
                        
                        else
                        
                            echo "Please select 1, 2, or 3." && sleep 3
                            clear && ask_user
                        
                        fi
                        }
                        
                        ask_user
                        

                        发布此方法是希望有人会发现它有用且节省时间。

                        【讨论】:

                          【解决方案20】:
                          inquire ()  {
                            echo  -n "$1 [y/n]? "
                            read answer
                            finish="-1"
                            while [ "$finish" = '-1' ]
                            do
                              finish="1"
                              if [ "$answer" = '' ];
                              then
                                answer=""
                              else
                                case $answer in
                                  y | Y | yes | YES ) answer="y";;
                                  n | N | no | NO ) answer="n";;
                                  *) finish="-1";
                                     echo -n 'Invalid response -- please reenter:';
                                     read answer;;
                                 esac
                              fi
                            done
                          }
                          
                          ... other stuff
                          
                          inquire "Install now?"
                          
                          ...
                          

                          【讨论】:

                          • 在每行的开头放置四个空格以保留代码的格式。
                          • 如果 case 开关是硬编码的,为什么我们提供 'y' 和 'n' 作为参数来询问()?那只是要求滥用。它们是固定参数,不可更改,因此第 2 行的 echo 应为: echo -n "$1 [Y/N]? " 它们无法更改,因此不应提供。
                          • @MyrddinEmrys 您能否详细说明您的评论?或者发布一篇文章或几个关键字的链接,以便我自己进行研究。
                          • @MateuszPiotrowski 自从我发表评论以来,答案已被编辑和改进。您可以单击上面的“已编辑的 12 月 23 日”链接以查看此答案的所有过去版本。早在 2008 年,代码就完全不同了。
                          【解决方案21】:

                          在这种情况下,我已经多次使用case 语句,使用case 语句是一个很好的方法。可以实现一个while 循环,它封装case 块,利用布尔条件,以便对程序进行更多控制,并满足许多其他要求。在满足所有条件后,可以使用break,它将控制权交还给程序的主要部分。此外,为了满足其他条件,当然可以在控制结构中添加条件语句:case 语句和可能的while 循环。

                          使用case 语句来满足您的请求的示例

                          #! /bin/sh 
                          
                          # For potential users of BSD, or other systems who do not
                          # have a bash binary located in /bin the script will be directed to
                          # a bourne-shell, e.g. /bin/sh
                          
                          # NOTE: It would seem best for handling user entry errors or
                          # exceptions, to put the decision required by the input 
                          # of the prompt in a case statement (case control structure), 
                          
                          echo Would you like us to perform the option: "(Y|N)"
                          
                          read inPut
                          
                          case $inPut in
                              # echoing a command encapsulated by 
                              # backticks (``) executes the command
                              "Y") echo `Do something crazy`
                              ;;
                              # depending on the scenario, execute the other option
                              # or leave as default
                              "N") echo `execute another option`
                              ;;
                          esac
                          
                          exit
                          

                          【讨论】:

                            【解决方案22】:

                            更通用的是:

                            function menu(){
                                title="Question time"
                                prompt="Select:"
                                options=("Yes" "No" "Maybe")
                                echo "$title"
                                PS3="$prompt"
                                select opt in "${options[@]}" "Quit/Cancel"; do
                                    case "$REPLY" in
                                        1 ) echo "You picked $opt which is option $REPLY";;
                                        2 ) echo "You picked $opt which is option $REPLY";;
                                        3 ) echo "You picked $opt which is option $REPLY";;
                                        $(( ${#options[@]}+1 )) ) clear; echo "Goodbye!"; exit;;
                                        *) echo "Invalid option. Try another one.";continue;;
                                     esac
                                 done
                                 return
                            }
                            

                            【讨论】:

                              【解决方案23】:

                              作为单行命令的朋友,我使用了以下命令:

                              while [ -z $prompt ]; do read -p "Continue (y/n)?" choice;case "$choice" in y|Y ) prompt=true; break;; n|N ) exit 0;; esac; done; prompt=;
                              

                              书面长格式,它的工作原理是这样的:

                              while [ -z $prompt ];
                                do read -p "Continue (y/n)?" choice;
                                case "$choice" in
                                  y|Y ) prompt=true; break;;
                                  n|N ) exit 0;;
                                esac;
                              done;
                              prompt=;
                              

                              【讨论】:

                              • 你能澄清一下提示变量的使用吗?在我看来,它就像是在一根衬里之后被擦掉的,那你怎么用这条线做任何事情呢?
                              • 提示在 while 循环后被擦除。因为我希望在之后初始化提示变量(因为我更频繁地使用该语句)。只有在输入 y|Y 时才会在 shell 脚本中包含这一行,如果输入 n|N 则退出,或者重复要求输入其他所有内容。
                              【解决方案24】:
                              read -e -p "Enter your choice: " choice
                              

                              -e 选项使用户可以使用箭头键编辑输入。

                              如果您想使用建议作为输入:

                              read -e -i "yes" -p "Enter your choice: " choice
                              

                              -i 选项打印提示性输入。

                              【讨论】:

                              • yap, -e -i 不能在 sh (Bourne shell) 中工作,但是这个问题被专门标记为 bash..
                              【解决方案25】:

                              一种简单的方法是使用 xargs -p 或 gnu parallel --interactive

                              我更喜欢 xargs 的行为,因为它像其他交互式 unix 命令一样在提示符后立即执行每个命令,而不是收集 yesses 以在最后运行。 (通过你想要的,你可以Ctrl-C。)

                              例如,

                              echo *.xml | xargs -p -n 1 -J {} mv {} backup/
                              

                              【讨论】:

                              • 不错,但xargs --interactive 仅限于是或否。只要这就是您所需要的就足够了,但是我最初的问题给出了一个包含三个可能结果的示例。我真的很喜欢它是可流式的;许多常见的场景都将受益于它的管道能力。
                              • 我明白了。我的想法是,“取消”意味着简单地停止所有进一步的执行,这通过 Ctrl-C 支持,但如果您需要在取消(或不取消)时执行更复杂的操作,这还不够。
                              【解决方案26】:

                              受@Mark 和@Myrddin 答案的启发,我为通用提示创建了这个函数

                              uniprompt(){
                                  while true; do
                                      echo -e "$1\c"
                                      read opt
                                      array=($2)
                                      case "${array[@]}" in  *"$opt"*) eval "$3=$opt";return 0;; esac
                                      echo -e "$opt is not a correct value\n"
                                  done
                              }
                              

                              像这样使用它:

                              unipromtp "Select an option: (a)-Do one (x)->Do two (f)->Do three : " "a x f" selection
                              echo "$selection"
                              

                              【讨论】:

                                【解决方案27】:
                                echo "Please enter some input: "
                                read input_variable
                                echo "You entered: $input_variable"
                                

                                【讨论】:

                                • 我不同意,因为它只实现了 DOS 中“是,否,取消”对话框的部分功能。它未能实现的部分是输入检查......循环直到收到有效答案。
                                • (最初的问题标题是“如何在 Linux shell 脚本中提示输入?”)
                                • 但是原始问题描述没有改变,并且总是要求对是/否/取消提示做出响应。标题已更新为比我原来的更清晰,但问题描述始终清晰(在我看来)。
                                【解决方案28】:

                                我建议你use dialog...

                                Linux Apprentice: Improve Bash Shell Scripts Using Dialog

                                dialog 命令允许在 shell 脚本中使用窗口框,使其使用更具交互性。

                                它简单易用,还有一个名为 gdialog 的 gnome 版本,它采用完全相同的参数,但在 X 上显示了 GUI 样式。

                                【讨论】:

                                【解决方案29】:

                                多选版本:

                                ask () {                        # $1=question $2=options
                                    # set REPLY
                                    # options: x=..|y=..
                                    while $(true); do
                                        printf '%s [%s] ' "$1" "$2"
                                        stty cbreak
                                        REPLY=$(dd if=/dev/tty bs=1 count=1 2> /dev/null)
                                        stty -cbreak
                                        test "$REPLY" != "$(printf '\n')" && printf '\n'
                                        (
                                            IFS='|'
                                            for o in $2; do
                                                if [ "$REPLY" = "${o%%=*}" ]; then
                                                    printf '\n'
                                                    break
                                                fi
                                            done
                                        ) | grep ^ > /dev/null && return
                                    done
                                }
                                

                                例子:

                                $ ask 'continue?' 'y=yes|n=no|m=maybe'
                                continue? [y=yes|n=no|m=maybe] g
                                continue? [y=yes|n=no|m=maybe] k
                                continue? [y=yes|n=no|m=maybe] y
                                $
                                

                                它会将REPLY 设置为y(在脚本内部)。

                                【讨论】:

                                  【解决方案30】:

                                  此解决方案读取单个字符并在是响应时调用函数。

                                  read -p "Are you sure? (y/n) " -n 1
                                  echo
                                  if [[ $REPLY =~ ^[Yy]$ ]]; then
                                      do_something      
                                  fi
                                  

                                  【讨论】:

                                  • @Jav echo 在您回复后打印一个换行符。没有它,下一个要打印的内容将在您在同一行的回复之后立即出现。尝试删除 echo 以亲自查看。