bash:在"select"提示符下按Enter键时选择默认情况

jon*_*iba 10 bash user-input interactive

我在这样的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
Run Code Online (Sandbox Code Playgroud)

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

mkl*_*nt0 7

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


背景资料

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

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

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

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

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


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

下面的函数模拟了什么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}"

}
Run Code Online (Sandbox Code Playgroud)

你可以这样称呼它:

# 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
Run Code Online (Sandbox Code Playgroud)

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

注意:这段代码并没有解决问题,而是展示了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
Run Code Online (Sandbox Code Playgroud)

笔记:

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

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


Ase*_*rre 6

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

要模拟的行为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\""
Run Code Online (Sandbox Code Playgroud)