bash:在“选择”提示中按下回车时,从大小写中选择默认值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42789273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 15:53:07  来源:igfitidea点击:

bash: choose default from case when enter is pressed in a "select" prompt

bashuser-inputinteractive

提问by joniba

I'm prompting questions in a bash script like this:

我在这样的 bash 脚本中提示问题:

optionsAudits=("Yep" "Nope")
    echo "Include audits?"
    select opt in "${optionsAudits[@]}"; do
        case $REPLY in
            1) includeAudits=true; break ;;
            2) includeAudits=false; break ;;
            "\n") echo "You pressed enter"; break ;; # <--- doesn't work
            *) echo "What's that?"; exit;;
        esac
    done

How can I select a default option when enter is pressed? The "\n"case does not catch the enter key.

按下回车键时如何选择默认选项?该"\n"案件不赶回车键。

采纳答案by mklement0

To complement Aserre's helpful answer, which explains the problem with your code and offers an effective workaround, with background information and a generic, reusable custom selectimplementation that allows empty input:

为了补充Aserre 的有用答案,它解释了您的代码的问题并提供了一种有效的解决方法,包括背景信息和select允许空输入的通用、可重用的自定义实现



Background information

背景资料

To spell it out explicitly: selectitselfignores empty input(just pressing Enter) and simply re-prompts- user code doesn't even get to run in response.

明确说明:select它本身会忽略空输入(只需按Enter)并简单地重新提示- 用户代码甚至不会响应运行。

In fact, selectuses the empty string to signal to user code that an invalidchoice was typed.
That is, if the output variable - $opt, int this case - is emptyinside the selectstatement, the implication is that an invalid choice index was typed by the user.

事实上,select使用空字符串向用户代码表明输入了无效选项
也就是说,如果输出变量 - 在$opt这种情况下为 int -在语句中为select,则暗示用户键入了无效的选择索引。

The output variable receives the chosen option's text- either 'Yep'or 'Nope'in this case - not the indextyped by the user.

输出变量接收所选择的选项的文本-无论是'Yep''Nope'在这种情况下-不是索引由用户键入。

(By contrast, your code examines $REPLYinstead of the output variable, which contains exactly what the user typed, which isthe indexin case of a valid choice, but may contain extra leading and trailing whitespace).

(相比之下,您的代码检查$REPLY而不是输出变量,它包含用户键入的内容,这有效选择情况下的索引,但可能包含额外的前导和尾随空格)。

Note that in the event that you didn'twant to allow empty input, you could simply indicate to the user in the prompt text that ^C(Ctrl+C) can be used to abort the prompt.

请注意,倘若你希望允许空的输入,你可以简单地表示在提示文本,用户^CCtrl+C)可以用来中止提示



Generic custom selectfunction that also accepts empty input

select也接受空输入的通用自定义函数

The following function closely emulates what selectdoes while also allowing empty input (just pressing Enter). Note that the function intercepts invalid input, prints a warning, and re-prompts:

以下函数密切模拟了select同时允许空输入(只需按Enter)的功能。注意该函数拦截无效输入,打印警告,并重新提示:

# Custom `select` implementation that allows *empty* input.
# Pass the choices as individual arguments.
# Output is the chosen item, or "", if the user just pressed ENTER.
# Example:
#    choice=$(selectWithDefault 'one' 'two' 'three')
selectWithDefault() {

  local item i=0 numItems=$# 

  # Print numbered menu items, based on the arguments passed.
  for item; do         # Short for: for item in "$@"; do
    printf '%s\n' "$((++i))) $item"
  done >&2 # Print to stderr, as `select` does.

  # Prompt the user for the index of the desired item.
  while :; do
    printf %s "${PS3-#? }" >&2 # Print the prompt string to stderr, as `select` does.
    read -r index
    # Make sure that the input is either empty or that a valid index was entered.
    [[ -z $index ]] && break  # empty input
    (( index >= 1 && index <= numItems )) 2>/dev/null || { echo "Invalid selection. Please try again." >&2; continue; }
    break
  done

  # Output the selected item, if any.
  [[ -n $index ]] && printf %s "${@: index:1}"

}

You could call it as follows:

你可以这样称呼它:

# Print the prompt message and call the custom select function.
echo "Include audits (default is 'Nope')?"
optionsAudits=('Yep' 'Nope')
opt=$(selectWithDefault "${optionsAudits[@]}")

# Process the selected item.
case $opt in
  'Yep') includeAudits=true; ;;
  ''|'Nope') includeAudits=false; ;; # $opt is '' if the user just pressed ENTER
esac


Optional reading: A more idiomatic version of your original code

可选阅读:原始代码的更惯用版本

Note: This code doesn't solve the problem, but shows more idiomatic use of the selectstatement; unlike the original code, this code re-displays the prompt if an invalid choice was made:

注意:这段代码并没有解决问题,而是展示了select语句的更惯用用法;与原始代码不同,如果做出无效选择,此代码将重新显示提示:

optionsAudits=("Yep" "Nope")
echo "Include audits (^C to abort)?"
select opt in "${optionsAudits[@]}"; do
    # $opt being empty signals invalid input.
    [[ -n $opt ]] || { echo "What's that? Please try again." >&2; continue; }
    break # a valid choice was made, exit the prompt.
done

case $opt in  # $opt now contains the *text* of the chosen option
  'Yep')
     includeAudits=true
     ;;
  'Nope') # could be just `*` in this case.
     includeAudits=false
     ;;
esac

Note:

笔记:

  • The casestatement was moved out of the selectstatement, because the latter now guarantees that only valid inputs can be made.

  • The casestatement tests the output variable($opt) rather than the raw user input ($REPLY), and that variable contains the choice text, not its index.

  • case语句已移出该select语句,因为后者现在保证只能进行有效输入。

  • case语句测试输出变量( $opt) 而不是原始用户输入 ( $REPLY),并且该变量包含选择文本,而不是其索引

回答by Aserre

Your problem is due to the fact that selectwill ignore empty input. For your case, readwill be more suitable, but you will lose the utility selectprovides for automated menu creation.

您的问题是由于select会忽略空输入。对于您的情况,read会更合适,但您将失去select用于自动创建菜单的实用程序。

To emulate the behaviour of select, you could do something like that :

要模拟 的行为select,您可以执行以下操作:

#!/bin/bash
optionsAudits=("Yep" "Nope")
while : #infinite loop. be sure to break out of it when a valid choice is made
do
    i=1
    echo "Include Audits?"
    #we recreate manually the menu here
    for o in  "${optionsAudits[@]}"; do
        echo "$i) $o"
        let i++
    done

    read reply
    #the user can either type the option number or copy the option text
    case $reply in
        "1"|"${optionsAudits[0]}") includeAudits=true; break;;
        "2"|"${optionsAudits[1]}") includeAudits=false; break;;
        "") echo "empty"; break;;
        *) echo "Invalid choice. Please choose an existing option number.";;
    esac
done
echo "choice : \"$reply\""

回答by blackghost

Updated answer:

更新的答案:

echo "Include audits? 1) Yep, 2) Nope"
read ans
case $ans in
    Yep|1  )  echo "yes"; includeAudits=true; v=1 ;;
    Nope|2 )  echo "no"; includeAudits=false; v=2 ;;
    ""     )  echo "default - yes"; includeAudits=true; v=1 ;;
    *      )  echo "Whats that?"; exit ;;
esac

This accepts either "Yep"or "1"or "enter"to select yes, and accepts "Nope"or "2"for no, and throws away anything else. It also sets v to 1 or 2 depending on whether the user wanted yes or no.

这接受或"Yep""1""enter"选择是,接受"Nope""2"否,并丢弃任何其他东西。它还根据用户是否想要是或否将 v 设置为 1 或 2。

回答by ajs117

This will do what you are asking for.

这将满足您的要求。

options=("option 1" "option 2");
while :
do
    echo "Select your option:"
    i=1;
    for opt in  "${options[@]}"; do
        echo "$i) $opt";
        let i++;
    done

    read reply
    case $reply in
        "1"|"${options[0]}"|"")
          doSomething1();
          break;;
        "2"|"${options[1]}")
          doSomething2();
          break;;
        *)
          echo "Invalid choice. Please choose 1 or 2";;
    esac
done