在 Bash 脚本中询问是/否问题
发布于 2009 年 6 月 26 日
为了避免这种常见的错误,我经常让我的 shell 脚本在继续之前提示我回答“是”或“否”。 这里描述的函数就是用来做这件事的:提出一个问题并验证答案。
该函数非常简单,它接受几个选项,其余参数被认为是问题文本。它提示用户输入答案,并验证答案是否为“yes”、“y”、“no”或“n”之一。答案被转换为小写,因此接受任何大小写组合。
可用的选项有--timeout N这会导致提示在 N 秒后超时,以及--default ANS这为超时的提示提供了一个默认答案。它还允许用户按 ENTER 并接受默认答案。 函数代码如下
#!/bin/bash.sh
#
#####################################################################
# Print warning message.
function warning()
{
echo "$*" >&2
}
#####################################################################
# Print error message and exit.
function error()
{
echo "$*" >&2
exit 1
}
#####################################################################
# Ask yesno question.
#
# Usage: yesno OPTIONS QUESTION
#
# Options:
# --timeout N Timeout if no input seen in N seconds.
# --default ANS Use ANS as the default answer on timeout or
# if an empty answer is provided.
#
# Exit status is the answer.
function yesno()
{
local ans
local ok=0
local timeout=0
local default
local t
while [[ "$1" ]]
do
case "$1" in
--default)
shift
default=$1
if [[ ! "$default" ]]; then error "Missing default value"; fi
t=$(tr '[:upper:]' '[:lower:]' <<<$default)
if [[ "$t" != 'y' && "$t" != 'yes' && "$t" != 'n' && "$t" != 'no' ]]; then
error "Illegal default answer: $default"
fi
default=$t
shift
;;
--timeout)
shift
timeout=$1
if [[ ! "$timeout" ]]; then error "Missing timeout value"; fi
if [[ ! "$timeout" =~ ^[0-9][0-9]*$ ]]; then error "Illegal timeout value: $timeout"; fi
shift
;;
-*)
error "Unrecognized option: $1"
;;
*)
break
;;
esac
done
if [[ $timeout -ne 0 && ! "$default" ]]; then
error "Non-zero timeout requires a default answer"
fi
if [[ ! "$*" ]]; then error "Missing question"; fi
while [[ $ok -eq 0 ]]
do
if [[ $timeout -ne 0 ]]; then
if ! read -t $timeout -p "$*" ans; then
ans=$default
else
# Turn off timeout if answer entered.
timeout=0
if [[ ! "$ans" ]]; then ans=$default; fi
fi
else
read -p "$*" ans
if [[ ! "$ans" ]]; then
ans=$default
else
ans=$(tr '[:upper:]' '[:lower:]' <<<$ans)
fi
fi
if [[ "$ans" == 'y' || "$ans" == 'yes' || "$ans" == 'n' || "$ans" == 'no' ]]; then
ok=1
fi
if [[ $ok -eq 0 ]]; then warning "Valid answers are: yes y no n"; fi
done
[[ "$ans" = "y" || "$ans" == "yes" ]]
}
if [[ $(basename "$0" .sh) == 'yesno' ]]; then
if yesno "Test bad timeout value? "; then
yesno --timeout none "Hello? "
fi
if yesno "Test timeout without default value? "; then
yesno --timeout 10 "Hello? "
fi
if yesno "Test bad default value? "; then
yesno --default none "Hello? "
fi
if yesno "Yes or no? "; then
echo "You answered yes"
else
echo "You answered no"
fi
if yesno --default yes "Yes or no (default yes) ? "; then
echo "You answered yes"
else
echo "You answered no"
fi
if yesno --default no "Yes or no (default no) ? "; then
echo "You answered yes"
else
echo "You answered no"
fi
if yesno --timeout 5 --default no "Yes or no (timeout 5, default no) ? "; then
echo "You answered yes"
else
echo "You answered no"
fi
if yesno --timeout 5 --default yes "Yes or no (timeout 5, default yes) ? "; then
echo "You answered yes"
else
echo "You answered no"
fi
fi
# vim: tabstop=4: shiftwidth=4: noexpandtab:
# kate: tab-width 4; indent-width 4; replace-tabs false;
代码以几个函数开始,这些函数打印警告和错误消息。主函数检查参数,然后循环直到收到有效答案。请注意,如果指定了超时并且输入了任何答案(有效或无效),则超时将被关闭。该函数的最后一行测试答案,查看其值是否为“yes”或“y”,从而设置函数的退出状态。
文件末尾的代码仅在您直接调用文件而不是将其源到 shell 脚本中时执行。 直接运行的输出如下
$ sh yesno.sh
Test bad timeout value? n
Test timeout without default value? n
Test bad default value? n
Yes or no? yep
Valid answers are: yes y no n
Yes or no? yes
You answered yes
Yes or no (default yes) ? <ENTER>
You answered yes
Yes or no (default no) ? <ENTER>
You answered no
Yes or no (timeout 5, default no) ? You answered no
Yes or no (timeout 5, default yes) ? You answered yes
请注意最后几行,没有提供答案,因此在超时后使用了默认值。 这就是为什么响应文本与问题出现在同一行上的原因。