linux shell 编程:select生成交互式菜单

介绍

select 是 bash 内置命令,用来快速生成交互式菜单,属于 shell 循环的一种,常和 case 搭配使用。

只在 bash / ksh 支持,sh(posix shell)没有 select。

语法

select 变量名 [in 选项列表]
do
    循环体
done

工作机制

  1. select 自动把 in 后面的每一项编号输出成菜单
  2. 打印提示符 PS3(默认 #?)等待用户输入数字
  3. 用户输入数字回车:
    • 对应选项赋值给【变量名】
    • 内置变量 REPLY 保存用户输入的内容
  4. 输入不在菜单的数字 → 变量为空,REPLY 保留输入值
  5. 直接回车 → 重复打印菜单
  6. 输入 Ctrl+C 退出循环

    ⚠️ select默认是无限循环,需要手动用 break 退出。

示例

#!/bin/bash
PS3="请选择一个选项: "  # 修改菜单提示符
select fruit in apple banana orange quit
do
    case $fruit in
        apple)
            echo "你选了苹果"
            ;;
        banana)
            echo "你选了香蕉"
            ;;
        orange)
            echo "你选了橙子"
            ;;
        quit)
            echo "退出菜单"
            break
            ;;
        *)
            echo "无效选项,请重新选择"
            ;;
    esac
done

运行效果:

1) apple
2) banana
3) orange
4) quit
请选择一个选项:

关键内置变量

  1. PS3:select 专用提示符,一定要在 select 之前定义
  2. REPLY:保存用户键盘输入的原始字符串(数字)
  3. 循环变量:保存选中的选项文本;选错时为空字符串

示例看 REPLY:

#!/bin/bash
PS3="输入编号 > "
select name in Alice Bob
do
    echo "REPLY=$REPLY"
    echo "name=$name"
    break
done

输入1:

REPLY=1
name=Alice

输入9:

REPLY=9
name=

省略 in 列表 的写法

select var 不带 in,则 使用位置参数 $@

#!/bin/bash
PS3="请选择:"
select arg
do
    echo "选中:$arg"
    break
done

执行:./test.sh a b c,菜单自动生成 a b c。

常见技巧

1. 菜单选项来自数组(推荐,动态菜单)

#!/bin/bash
opts=("重启服务" "停止服务" "查看状态" "退出")
PS3="请输入编号:"
select opt in "${opts[@]}"; do
    case $opt in
        "重启服务") echo "restart" ;;
        "停止服务") echo "stop" ;;
        "查看状态") echo "status" ;;
        "退出") break ;;
        *) echo "错误输入" ;;
    esac
done

2. 只允许数字,限制输入

#!/bin/bash
PS3="请选择:"
select o in one two three exit
do
    if ! [[ $REPLY =~ ^[0-9]+$ ]];then
        echo "请输入数字编号"
        continue
    fi
    case $o in
        exit) break;;
        *) echo "选中 $o";;
    esac
done

3. 一次性菜单,选择后自动退出(不循环)

把 break 放到每个分支,或者根据业务逻辑跳出。

select 退出方式

  1. break:退出 select 循环(最常用)
  2. exit:直接退出整个脚本
  3. Ctrl + C:SIGINT 终止脚本

注意坑点

  1. select 不会自动退出,忘记写 break 就无限循环
  2. 用户输入的是数字编号,不是选项文字!不要拿 REPLY 和选项字符串比较
  3. 菜单每次循环都会重新打印;如果不想重复打印菜单,可以清空 PS3,或者把菜单放到循环外面自己打印
  4. 数组展开务必写 "${arr[@]}",防止带空格的选项被拆分
  5. POSIX sh 不支持 select,脚本开头必须 #!/bin/bash