bash:在"select"提示符中按 Enter 时从大小写中选择默认值



我在这样的 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

按下回车键时如何选择默认选项?"n"案例无法捕获回车键。

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

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

实际上,select使用空字符串向用户代码发出信号,表明键入了无效的选项.
也就是说,如果输出变量 -$opt,在本例中为 int - 在select语句中为,则意味着用户键入了无效的选择索引。

输出变量接收所选选项的文本(在本例中为'Yep''Nope'),而不是用户键入索引

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

请注意,如果您不想允许空输入,您可以只需在提示文本中向用户指示可以使用^C(Ctrl+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 '%sn' "$((++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}"
}

您可以按如下方式调用它:

# 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

让函数本身处理默认逻辑替代实现谢谢,RL-S

此实现在两个方面与上述实现不同:

  • 允许您在选项中指定默认值,方法是在它前面加上!否则第一个选项将成为默认值。默认选项打印时带有尾随[default](并且不带前导!)。然后,该函数将空输入转换为默认选项。

  • 所选选项将作为基于1索引而不是文本返回。换句话说:您可以假设在函数返回时做出了有效的选择,并且该选择由其在给定选项中的位置指示。

# Custom `select` implementation with support for a default choice
# that the user can make by pressing just ENTER.
# Pass the choices as individual arguments; e.g. `selectWithDefault Yes No``
# The first choice is the default choice, unless you designate
# one of the choices as the default with a leading '!', e.g.
# `selectWithDefault Yes !No`
# The default choice is printed with a trailing ' [default]'
# Output is the 1-based *index* of the selected choice, as shown
# in the UI.
# Example:
#    choice=$(selectWithDefault 'Yes|No|!Abort' )
selectWithDefault() {
local item i=0 numItems=$# defaultIndex=0
# Print numbered menu items, based on the arguments passed.
for item; do         # Short for: for item in "$@"; do
[[ "$item" == !* ]] && defaultIndex=$(( $i + 1)) && item="${item:1} [default]"
printf '%sn' "$((++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 ]] && index=$defaultIndex && break  # empty input == default choice  
(( index >= 1 && index <= numItems )) 2>/dev/null || { echo "Invalid selection. Please try again." >&2; continue; }
break
done
# Output the selected *index* (1-based).
printf $index
}

示例调用:

# Print the prompt message and call the custom select function,
# designating 'Abort' as the default choice.
echo "Include audits?"
ndx=$(selectWithDefault 'Yes' 'No', '!Abort')
case $ndx in
1) echo "include";;
2) echo "don't include";;
3) echo "abort";;
esac

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

注意:此代码不能解决问题,但显示了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

注意:

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

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

您的问题是由于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""

更新的答案:

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

这接受"Yep""1""enter"选择"是",接受"Nope"或"否""2",并丢弃其他任何内容。 它还将 v 设置为 1 或 2,具体取决于用户是想要是还是否。

这将满足您的要求。

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

假设您的默认选项是Yep

#!/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 -rp "Audit option: " -iYep
#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;;
"") includeAudits=true; break;;
*) echo "Invalid choice. Please choose an existing option number.";;
esac
done
echo "choice : "$REPLY""
echo "includeAudits : "$includeAudits""

注意到这行:

read -rp "Audit option: " -eiYep

我也拉起$reply$REPLY,以便案件判决更好地工作。

现在,按 Enter 后,输出将如下所示:

Include Audits?: 
1) Yep
2) Nope
Audit option: Yep
choice : ""
includeAudits : "true"
# 

作为对select的增强,read -eiYep将预先向输入缓冲区提供Yep默认值。

使用默认值的唯一缺点是必须按几次退格键才能输入自己的答案。

相关内容

  • 没有找到相关文章

最新更新