在Linux shell脚本中如何提示用户输入Yes/No/Cancel选项

在Linux shell脚本中如何提示用户输入Yes/No/Cancel选项

技术背景

在Linux shell脚本编写过程中,经常需要与用户进行交互,提示用户输入Yes/No/Cancel选项以决定脚本的后续操作。不同的场景和需求可能需要不同的实现方式,因此掌握多种提示用户输入的方法是很有必要的。

实现步骤

1. 使用read命令

1
2
3
4
5
6
7
8
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

上述代码通过read命令提示用户输入,使用case语句对用户输入进行判断,根据不同的输入执行相应的操作。

2. 使用Bash的select命令

1
2
3
4
5
6
7
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命令会显示可用的选项,用户只需输入对应的数字即可选择,无需手动输入选项内容,并且会自动循环处理无效输入。

3. 实现国际化支持

1
2
3
4
5
6
7
8
9
set -- $(locale LC_MESSAGES)
yesexpr="$1"; noexpr="$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

通过locale命令获取当前语言环境下的Yes和No的表达式和字符串,实现脚本的国际化支持。

4. POSIX通用解决方案

4.1 基本readif...then...else组合

1
2
3
4
5
6
7
8
9
#!/bin/sh
printf 'Is this a good question (y/n)? '
read answer

if [ "$answer" != "${answer#[Yy]}" ] ;then
echo Yes
else
echo No
fi

4.2 单键输入功能

1
2
3
4
5
6
7
8
9
#!/bin/sh
printf 'Is this a good question (y/n)? '
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg
if [ "$answer" != "${answer#[Yy]}" ];then
echo Yes
else
echo No
fi

4.3 单键输入功能并支持国际化

1
2
3
4
5
6
7
8
9
10
#!/bin/sh
yExpr=$(locale yesexpr)
printf 'Is this a good question (y/n)? '
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg
if [ "$answer" != "${answer#${yExpr#^}}" ];then
echo Yes
else
echo No
fi

4.4 封装成函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/sh
askFor() {
__aF_yExpr="$(locale yesexpr)" __aF_nExpr="$(locale noexpr)"
printf '%s? ' "$*"
__aF_oStty=$(stty -g)
stty raw -echo
__aF_key=$( while ! head -c 1| grep "$__aF_yExpr\|$__aF_nExpr"; do :;done )
stty $__aF_oStty
if [ "$__aF_key" != "${__aF_key#${__aF_yExpr#^}}" ]; then echo Yes
else echo No; return 1
fi
}

verbEcho() { [ "$quietMode" -gt 0 ] || echo $*;}
askFor Enable verbose mode
quietMode=$?

verbEcho Ask for continuing
askFor Do you want to continue this demonstration || exit

toInstall=''
for package in moon-buggy pacman4console junior-games-text; do
verbEcho Ask for Installation of $package
if askFor Do I install full "$package"; then
verbEcho "Add $package to list"
toInstall="$toInstall $package"
fi
done
if [ -z "$toInstall" ]; then
echo Nothing to do.
elif askFor Do you really want to install $toInstall; then
verbEcho Proceed installation of $toInstall
echo sudo apt install $toInstall # Drop `echo` for real installation
fi

5. 使用专用工具

1
2
3
4
5
if whiptail --yesno "Is this a good question" 20 60 ;then
echo Yes
else
echo No
fi

可以使用whiptaildialoggdialogkdialog等工具创建更美观的交互界面。

6. Bash特定解决方案

6.1 基本的行内方法

1
2
3
4
5
6
7
8
9
read -p "Is this a good question (y/n)? " answer
case ${answer:0:1} in
y|Y )
echo Yes
;;
* )
echo No
;;
esac

6.2 单键输入功能

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

6.3 带超时和倒计时功能

1
2
3
i=6 ;while ((i-->1)) &&
! read -sn 1 -t 1 -p $'\rIs this a good question (Y/n)? '$i$'..\e[3D' answer;do
:;done ;[[ $answer == [nN] ]] && answer=No || answer=Yes ;echo "$answer "

7. 专用工具的高级用法

7.1 菜单选择

1
dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe

7.2 进度条

1
2
3
4
5
6
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
)

8. 使用readline的历史记录功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/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

9. 使用fzf工具

1
2
3
if [[ $(fzf --header='Delete current directory?' --tac <<<$'Yes\nNo') == Yes ]]; then
echo rm .
fi

核心代码

以下是一个封装好的Yes/No/Cancel提示函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/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

最佳实践

  • 封装函数:将常用的提示逻辑封装成函数,提高代码的复用性。
  • 支持国际化:使用locale命令获取当前语言环境下的Yes和No的表达式和字符串,使脚本支持多语言。
  • 错误处理:对用户的无效输入进行处理,确保脚本的健壮性。

常见问题

  • 输入验证问题:用户可能输入无效的选项,需要在脚本中进行验证和提示用户重新输入。
  • 国际化支持问题:不同的系统和语言环境可能对locale命令的支持不同,需要进行充分的测试。
  • 工具兼容性问题:使用专用工具(如whiptaildialog等)时,不同的系统可能没有安装这些工具,需要进行兼容性检查。

在Linux shell脚本中如何提示用户输入Yes/No/Cancel选项
https://119291.xyz/posts/how-to-prompt-for-yes-no-cancel-input-in-linux-shell-script/
作者
ww
发布于
2025年5月26日
许可协议