Linux Shell 文本菜单开发指南:从基础布局到 select 命令实战
概述
在编写 Shell 脚本时,“交互式” 是提升用户体验的核心 —— 尤其是针对运维脚本、工具类脚本,用户往往需要清晰的操作指引,而非记忆复杂的命令行参数。
文本菜单的核心价值在于:
- 降低使用门槛:通过可视化列表告知用户 “脚本能做什么”,避免用户因不熟悉参数导致操作失误;
- 统一操作逻辑:将多个功能(如磁盘查看、内存监控)聚合到同一入口,用户只需选择数字 / 选项即可触发;
- 提升专业性:标准化的菜单布局(如标题、选项、提示符)让脚本更像 “工具” 而非 “临时脚本”。
Shell 实现文本菜单的核心技术是 case 命令(处理用户选择)和 select 命令(快速生成菜单),下文将从基础到进阶逐步讲解。
一、基础文本菜单:手动构建布局与逻辑
手动构建菜单的优势是 “完全自定义”—— 可自由控制菜单样式、交互流程,适合对界面有个性化需求的场景。核心步骤分为:创建菜单布局、封装功能函数、编写选择逻辑。
1. 第一步:设计菜单布局
菜单布局的核心是 “清空屏幕 + 格式化输出”,需用到两个关键工具:
- clear 命令:清空终端屏幕,避免历史输出干扰菜单显示;
- echo -e 选项:支持转义字符(如 \t 制表符、\n 换行符),实现对齐排版。
示例:基础菜单布局代码
#!/bin/bash
# 脚本功能:演示基础菜单布局
clear # 清空屏幕,准备显示菜单
# 用 echo -e 实现格式化输出(\t 缩进,\n 换行)
echo -e "\t\t\t=== 系统管理菜单 ===\n" # 菜单标题(居中对齐)
echo -e "\t1. 查看磁盘空间(df -k)"
echo -e "\t2. 查看当前登录用户(who)"
echo -e "\t3. 查看内存使用(/proc/meminfo)"
echo -e "\t0. 退出菜单\n"
# -en 选项:不换行(让光标停在提示符后,提升交互感)
echo -en "\t请输入选项 [0-3]:"
# 读取用户输入(-n 1 限制只读1个字符,无需按Enter)
read -n 1 option
# 换行(避免后续输出与提示符重叠)
echo -e "\n"
执行效果(界面整洁,选项对齐):
=== 系统管理菜单 ===
1. 查看磁盘空间(df -k)
2. 查看当前登录用户(who)
3. 查看内存使用(/proc/meminfo)
0. 退出菜单
请输入选项 [0-3]:1
2. 第二步:封装菜单功能为函数
菜单的每个选项对应一个 “功能”(如查看磁盘、查看内存),将这些功能封装为 Shell 函数,可让代码更清晰、易维护 —— 后续修改功能只需调整函数内部,无需改动菜单逻辑。
最佳实践:先写 “桩函数”,再补实现
若功能暂未完成,可先写 “桩函数”(空函数或占位提示),保证菜单能正常运行。例如:
#!/bin/bash
# 1. 磁盘空间查看函数
function disk_space {
clear # 清空屏幕,单独显示功能结果
echo -e "\t=== 磁盘空间信息(df -k)===\n"
df -k # 核心命令:查看磁盘空间(KB单位)
}
# 2. 登录用户查看函数
function logged_users {
clear
echo -e "\t=== 当前登录用户(who)===\n"
who # 核心命令:查看登录用户
}
# 3. 内存使用查看函数(桩函数示例,暂未实现)
function mem_usage {
clear
echo -e "\t=== 内存使用信息(待实现)===\n"
echo "提示:后续将替换为 cat /proc/meminfo 或 free 命令"
}
# 4. 菜单显示函数(将布局封装为函数,方便重复调用)
function show_menu {
clear
echo -e "\t\t\t=== 系统管理菜单 ===\n"
echo -e "\t1. 查看磁盘空间"
echo -e "\t2. 查看当前登录用户"
echo -e "\t3. 查看内存使用(待实现)"
echo -e "\t0. 退出菜单\n"
echo -en "\t请输入选项 [0-3]:"
read -n 1 option
echo -e "\n"
}
3. 第三步:编写菜单核心逻辑(case 命令)
菜单的 “交互逻辑” 由 case 命令实现:根据用户输入的 option(如 1、2、0),调用对应的函数;若输入无效(如 4、a),则提示错误。
完整示例:布局 + 函数 + 逻辑整合
#!/bin/bash
# 系统管理菜单脚本(基础版)
# -------------------------- 功能函数 --------------------------
# 1. 查看磁盘空间
function disk_space {
clear
echo -e "\t=== 磁盘空间信息(df -k)===\n"
df -k
}
# 2. 查看登录用户
function logged_users {
clear
echo -e "\t=== 当前登录用户(who)===\n"
who
}
# 3. 查看内存使用
function mem_usage {
clear
echo -e "\t=== 内存使用信息(cat /proc/meminfo)===\n"
cat /proc/meminfo | head -10 # 只显示前10行,避免输出过长
}
# 4. 显示菜单
function show_menu {
clear
echo -e "\t\t\t=== 系统管理菜单 ===\n"
echo -e "\t1. 查看磁盘空间"
echo -e "\t2. 查看当前登录用户"
echo -e "\t3. 查看内存使用"
echo -e "\t0. 退出菜单\n"
echo -en "\t请输入选项 [0-3]:"
read -n 1 option
echo -e "\n"
}
# -------------------------- 核心逻辑 --------------------------
# 无限循环(直到用户选择0退出)
while true; do
# 1. 显示菜单,获取用户选择
show_menu
# 2. 根据选择执行对应功能
case $option in
0) # 退出菜单
clear
echo -e "\t=== 感谢使用,再见!===\n"
break # 跳出循环,结束脚本
;;
1) # 查看磁盘空间
disk_space
;;
2) # 查看登录用户
logged_users
;;
3) # 查看内存使用
mem_usage
;;
*) # 无效输入
clear
echo -e "\t=== 错误:请输入 0-3 之间的选项!===\n"
;;
esac
# 3. 功能执行后,等待用户按任意键返回菜单
echo -en "\t按任意键返回菜单..."
read -n 1 # 无需存储输入,仅用于暂停
echo -e "\n"
done
执行流程解析:
- 脚本启动后进入 while true 无限循环,持续显示菜单;
- 用户选择选项后,case 命令调用对应函数,显示功能结果;
- 功能执行完毕后,等待用户按任意键,再次显示菜单;
- 选择 “0” 时,break 跳出循环,脚本结束。
执行效果(选择 “1” 查看磁盘空间):
=== 系统管理菜单 ===
1. 查看磁盘空间
2. 查看当前登录用户
3. 查看内存使用
0. 退出菜单
请输入选项 [0-3]:1
=== 磁盘空间信息(df -k)===
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 515023616 8934528 480445664 2% /
tmpfs 8129440 0 8129440 0% /dev/shm
/dev/sdb1 1048575968 1234560 1047341408 1% /data
按任意键返回菜单...
二、进阶:用 select 命令快速生成菜单
手动构建菜单需要写 “布局 + 读取输入 + 循环”,步骤较繁琐。Bash 提供 select 命令—— 只需一行代码即可自动生成带编号的菜单,自动处理用户输入,大幅简化开发。
1. select 命令核心语法
select 命令会自动完成 “菜单显示 + 输入读取 + 输入验证”,语法格式如下:
# 1. (可选)设置菜单提示符(默认是 #? ,用 PS3 变量自定义)
PS3="请输入选项编号:"
# 2. select 命令主体
select 变量名 in 选项1 选项2 选项3...
do
# 根据变量值执行对应逻辑(通常用 case 命令)
case $变量名 in
选项1) 命令1 ;;
选项2) 命令2 ;;
选项3) 命令3 ;;
*) 提示无效输入 ;;
esac
done
关键特性:
- 自动为每个选项分配 “数字编号”(1、2、3...);
- 自动显示 PS3 定义的提示符(如 “请输入选项编号:”);
- 用户输入编号后,变量会存储 “选项的完整字符串”(而非编号);
- 支持按 Ctrl+C 退出菜单。
2. 示例:用 select 实现系统管理菜单
基于前文的功能函数,用 select 重构菜单逻辑,代码量减少 50%:
#!/bin/bash
# 系统管理菜单脚本(select 进阶版)
# -------------------------- 功能函数 --------------------------
function disk_space {
clear
echo -e "\t=== 磁盘空间信息(df -k)===\n"
df -k
}
function logged_users {
clear
echo -e "\t=== 当前登录用户(who)===\n"
who
}
function mem_usage {
clear
echo -e "\t=== 内存使用信息(free -h)===\n" # -h 人类可读格式
free -h
}
# -------------------------- select 菜单 --------------------------
# 1. 自定义菜单提示符(默认是 #?)
PS3="请输入选项编号(按Ctrl+C退出):"
# 2. 生成菜单(选项列表用空格分隔)
select menu_option in "查看磁盘空间" "查看当前登录用户" "查看内存使用" "退出菜单"
do
# 根据选项字符串执行逻辑(注意:变量存的是完整字符串,不是编号)
case $menu_option in
"查看磁盘空间")
disk_space
;;
"查看当前登录用户")
logged_users
;;
"查看内存使用")
mem_usage
;;
"退出菜单")
clear
echo -e "\t=== 感谢使用,再见!===\n"
exit 0 # 退出脚本
;;
*) # 无效输入(如输入非数字)
clear
echo -e "\t=== 错误:请输入正确的选项编号!===\n"
;;
esac
# 功能执行后,等待用户按任意键返回菜单
echo -en "\t按任意键返回菜单..."
read -n 1
echo -e "\n"
done
执行效果(自动生成编号,交互更简洁):
1) 查看磁盘空间 3) 查看内存使用
2) 查看当前登录用户 4) 退出菜单
请输入选项编号(按Ctrl+C退出):2
=== 当前登录用户(who)===
root pts/0 2024-06-13 10:30 (192.168.1.100)
user1 pts/1 2024-06-13 11:15 (192.168.1.101)
按任意键返回菜单...
3. select 命令的优势与注意事项
优势:
- 开发效率高:无需手动写布局、读取输入,一行 select 搞定菜单生成;
- 用户体验好:自动编号,用户只需记编号(如 1、2),无需记选项字符串;
- 兼容性强:所有 Bash 环境均支持,无需依赖外部工具。
注意事项: - 变量存储的是 “选项字符串”:case 命令中必须用完整的选项字符串(如 "查看磁盘空间")匹配,而非编号;
- 默认不支持 “单字符输入无需 Enter”:用户输入编号后需按 Enter 确认(若需此功能,仍需用 read -n 1 手动实现);
- 退出方式:需在选项中显式添加 “退出” 逻辑(如 exit 0),或让用户按 Ctrl+C 强制退出。
三、文本菜单的优化技巧
无论用手动布局还是 select 命令,以下优化技巧能让菜单脚本更专业、更易用:
1. 菜单标题与版本信息
在菜单顶部添加 “脚本名称、版本、功能说明”,方便用户了解脚本用途。例如:
function show_menu {
clear
echo -e "\t\t=== 系统管理工具 v1.0 ===\n"
echo -e "\t功能:快速查看系统磁盘、用户、内存信息\n"
echo -e "\t1. 查看磁盘空间"
echo -e "\t2. 查看当前登录用户"
echo -e "\t3. 查看内存使用"
echo -e "\t0. 退出菜单\n"
}
2. 输入验证:限制选项范围
手动布局时,可通过 if 语句提前验证输入是否在有效范围内(如 0-3),避免无效输入进入 case 命令:
function show_menu {
clear
# ... 菜单显示代码 ...
read -n 1 option
echo -e "\n"
# 验证输入是否为 0-3 的数字
if ! [[ $option =~ ^[0-3]$ ]]; then
option="*" # 设为无效值,让 case 命令处理错误
fi
}
3. 功能结果分页显示
若功能输出过长(如 cat /proc/meminfo),可通过 | less 实现分页,避免信息刷屏:
function mem_usage {
clear
echo -e "\t=== 内存使用信息(分页显示)===\n"
cat /proc/meminfo | less # 分页显示,按 q 退出分页
}
4. 脚本注释与帮助信息
在脚本开头添加注释,说明脚本功能、使用方法、依赖命令,方便后续维护:
#!/bin/bash
# 脚本名称:sys_admin_menu.sh
# 功能描述:系统管理菜单脚本,支持查看磁盘、用户、内存信息
# 使用方法:
# 1. 赋予执行权限:chmod +x sys_admin_menu.sh
# 2. 运行脚本:./sys_admin_menu.sh
# 依赖命令:df、who、free、cat(Linux 系统默认自带)
# 版本信息:v1.0(2024-06-13)
四、总结:两种菜单实现方式的对比与选择
手动布局和 select 命令各有适用场景,需根据需求选择:
对比维度 | 手动布局(echo + case) | select 命令 |
---|---|---|
灵活性 | 高(可自定义样式、交互逻辑) | 中(样式固定,自动编号) |
开发效率 | 低(需写布局、读取输入、循环) | 高(一行命令生成菜单) |
交互体验 | 可实现 “单字符无需 Enter” | 需按 Enter 确认输入 |
适用场景 | 1. 对界面样式有定制需求(如居中标题、特殊符号装饰) 2. 需要 “单字符快速选择”(无需按 Enter) 3. 菜单逻辑复杂(如多级菜单、条件显示选项) | 1. 快速开发工具类脚本(无需纠结样式) 2. 菜单选项简单、无复杂交互需求 3. 追求开发效率,优先实现功能而非界面 |
五、实战案例:多级文本菜单(进阶需求)
在实际场景中,单一菜单可能无法满足需求(如 “系统管理” 包含 “硬件监控”“用户管理” 等子功能),此时需要实现 多级菜单—— 通过 “主菜单 → 子菜单 → 功能” 的层级结构,聚合更多功能。
案例需求
实现一个 “Linux 系统工具箱”,包含两级菜单:
- 主菜单:硬件监控、用户管理、退出工具
- 硬件监控子菜单:查看磁盘、查看内存、返回主菜单
- 用户管理子菜单:查看登录用户、创建用户(需 root 权限)、返回主菜单
完整代码实现
#!/bin/bash
# 脚本名称:linux_system_toolbox.sh
# 功能描述:Linux 系统工具箱(多级菜单示例)
# 使用方法:chmod +x linux_system_toolbox.sh && ./linux_system_toolbox.sh
# 依赖命令:df、free、who、useradd(创建用户需root权限)
# 版本信息:v1.0(2024-06-13)
# -------------------------- 工具函数 --------------------------
# 1. 暂停并等待用户按任意键
function pause {
echo -en "\n\t按任意键返回上一级菜单..."
read -n 1
echo -e "\n"
}
# -------------------------- 硬件监控功能 --------------------------
function check_disk {
clear
echo -e "\t=== 硬件监控 → 磁盘空间(df -h)===\n"
df -h # 人类可读格式(GB/MB)
pause
}
function check_mem {
clear
echo -e "\t=== 硬件监控 → 内存使用(free -h)===\n"
free -h
pause
}
# 硬件监控子菜单
function hardware_menu {
while true; do
clear
echo -e "\t\t=== 系统工具箱 → 硬件监控子菜单 ===\n"
echo -e "\t1. 查看磁盘空间(df -h)"
echo -e "\t2. 查看内存使用(free -h)"
echo -e "\t0. 返回主菜单\n"
echo -en "\t请输入选项 [0-2]:"
read -n 1 option
echo -e "\n"
# 验证输入(仅允许 0-2)
if ! [[ $option =~ ^[0-2]$ ]]; then
option="*"
fi
# 子菜单逻辑
case $option in
0) break ;; # 返回主菜单(跳出子菜单循环)
1) check_disk ;;
2) check_mem ;;
*)
clear
echo -e "\t=== 错误:请输入 0-2 之间的选项!===\n"
pause
;;
esac
done
}
# -------------------------- 用户管理功能 --------------------------
function list_users {
clear
echo -e "\t=== 用户管理 → 当前登录用户(who)===\n"
who
pause
}
function create_user {
clear
# 检查是否为 root 用户(创建用户需 root 权限)
if [ "$(id -u)" -ne 0 ]; then
echo -e "\t=== 错误:创建用户需 root 权限,请用 sudo 运行!===\n"
pause
return
fi
# 读取用户名并创建
echo -en "\t请输入要创建的用户名:"
read username
if id "$username" &>/dev/null; then
echo -e "\n\t=== 提示:用户 $username 已存在!===\n"
else
useradd -m "$username" # -m 自动创建用户家目录
if [ $? -eq 0 ]; then
echo -e "\n\t=== 成功:用户 $username 创建完成!===\n"
echo -e "\t提示:请用 passwd $username 设置密码\n"
else
echo -e "\n\t=== 错误:用户 $username 创建失败!===\n"
fi
fi
pause
}
# 用户管理子菜单
function user_menu {
while true; do
clear
echo -e "\t\t=== 系统工具箱 → 用户管理子菜单 ===\n"
echo -e "\t1. 查看当前登录用户"
echo -e "\t2. 创建新用户(需root权限)"
echo -e "\t0. 返回主菜单\n"
echo -en "\t请输入选项 [0-2]:"
read -n 1 option
echo -e "\n"
if ! [[ $option =~ ^[0-2]$ ]]; then
option="*"
fi
case $option in
0) break ;;
1) list_users ;;
2) create_user ;;
*)
clear
echo -e "\t=== 错误:请输入 0-2 之间的选项!===\n"
pause
;;
esac
done
}
# -------------------------- 主菜单 --------------------------
function main_menu {
while true; do
clear
echo -e "\t\t\t=== Linux 系统工具箱 v1.0 ===\n"
echo -e "\t1. 硬件监控(磁盘、内存)"
echo -e "\t2. 用户管理(查看、创建)"
echo -e "\t0. 退出工具箱\n"
echo -en "\t请输入选项 [0-2]:"
read -n 1 option
echo -e "\n"
if ! [[ $option =~ ^[0-2]$ ]]; then
option="*"
fi
case $option in
0)
clear
echo -e "\t=== 感谢使用,再见!===\n"
exit 0
;;
1) hardware_menu ;; # 进入硬件监控子菜单
2) user_menu ;; # 进入用户管理子菜单
*)
clear
echo -e "\t=== 错误:请输入 0-2 之间的选项!===\n"
pause
;;
esac
done
}
# -------------------------- 脚本入口 --------------------------
main_menu
核心设计思路
- 层级循环嵌套:主菜单用 while true 循环,子菜单也用独立 while true 循环 —— 选择 “返回主菜单” 时,用 break 跳出子菜单循环,回到主菜单;
- 公共函数复用:将 “暂停等待” 封装为 pause 函数,避免重复代码;
- 权限与合法性检查:创建用户前检查是否为 root 权限,创建时检查用户是否已存在,提升脚本健壮性;
- 输入验证前置:每个菜单都先验证输入是否在有效范围,避免无效值进入 case 命令。
六、最终建议
- 优先用 select 快速验证功能:开发初期若只需 “功能可用”,用 select 命令快速搭建菜单框架,后续再优化界面;
- 复杂场景用手动布局 + 多级循环:需自定义样式、多级菜单时,采用 “函数封装 + 循环嵌套”,保证代码清晰;
- 重视用户体验细节:添加暂停等待、输入验证、权限提示,让脚本从 “能用” 升级为 “好用”;
- 规范脚本文档:开头添加功能、用法、依赖说明,方便自己和他人维护。
通过本文的方法,你可以轻松实现从 “简单单级菜单” 到 “复杂多级工具” 的 Shell 脚本,满足日常运维、自动化管理的大部分交互需求。