Shell 脚本编程:条件判断与循环
本文以实践为主,讲解 Shell 脚本中最常用的流程控制语句:if、case、while/until、for/select,以及 Shell 函数。每个知识点只保留关键示例,并对示例代码逐行注释。
示例运行提示符统一为
[root@server ~ 21:36:47]#。
一、if 条件语句
if 是运维脚本里使用最频繁的语句,语义相当于"如果...那么"。<条件表达式> 成立(真)时执行 then 后面的指令,否则跳过。
1.1 三种分支结构
单分支(if...then)
bash
if <条件表达式>;then
指令...
fi
双分支(if...then...else)
bash
if <条件表达式>;then
指令1
else
指令2
fi
多分支(if...elif...else)
bash
if <条件表达式1>;then
指令1
elif <条件表达式2>;then
指令2
else
指令3
fi
注意:每个
elif都要带then,结尾的else不带then,每个if都要有对应的fi收尾。
1.2 条件表达式的五种写法
<条件表达式> 可用以下任意一种,效果一致,选一种自己顺手的即可:
| 写法 | 示例 | 说明 |
|---|---|---|
test |
if test -d /tmp;then |
老式 test 命令 |
[] |
if [ -d /tmp ];then |
最常用,注意 [ 前后留空格 |
[[]] |
if [[ $a == b ]];then |
增强版,支持 =~ 正则 |
(()) |
if ((i>0));then |
专用于算术比较 |
| 命令 | if systemctl is-active sshd;then |
直接以命令退出码做判断 |
<条件表达式> 的本质是判断"真 / 假",Linux 里判定的来源有两个:一是命令退出码 ($?,0 表示成功=真,非 0 表示失败=假),二是比较运算符的计算结果。常用比较运算符速查:
| 类别 | 运算符 | 含义 |
|---|---|---|
| 数值比较 | -eq -ne -gt -lt -ge -le |
等于 / 不等于 / 大于 / 小于 / 大于等于 / 小于等于 |
| 字符串比较 | = != -z -n |
相等 / 不相等 / 长度为 0 / 长度非 0 |
| 文件判断 | -e -f -d -r -w -x |
存在 / 普通文件 / 目录 / 可读 / 可写 / 可执行 |
| 逻辑运算 | -a -o ! |
与 / 或 / 非([] 内用;[[]] 可用 && ` |
1.3 关键示例
示例 1(单分支):检测 sshd 是否运行,未运行则启动
bash
[root@server ~ 21:38:14]# cat check_sshd1.sh
#!/bin/bash
# 检测 sshd 是否运行,is-active 输出丢弃,只看退出码
systemctl is-active sshd &>/dev/null
if [ $? -ne 0 ];then # $? 是上一条命令退出码,非 0 表示 sshd 未运行
echo "sshd is not running, I'll start sshd."
systemctl start sshd # 启动 sshd
fi
示例 2(双分支):检测 sshd,运行与未运行分别提示
bash
[root@server ~ 21:39:24]# cat check_sshd2.sh
#!/bin/bash
systemctl is-active sshd &>/dev/null
if [ $? -ne 0 ];then # 未运行分支
echo "sshd is not running."
systemctl start sshd # 启动并提示 DONE
echo "sshd started: DONE"
else # 已运行分支
echo "sshd is running"
fi
示例 3(多分支):通过传参控制 sshd
bash
[root@server ~ 21:40:21]# cat ctrl_sshd.sh
#!/bin/bash
action="$1" # 取第一个命令行参数,作为要执行的动作
if [ "$action" = "start" ];then
systemctl start sshd
elif [ "$action" = "stop" ];then
systemctl stop sshd
elif [ "$action" = "status" ];then
systemctl status sshd
elif [ "$action" = "restart" ];then
systemctl restart sshd
else # 参数不合法时,打印用法提示
echo "Usage: $0 start|stop|status|restart"
fi
示例 4(经典):三个数从大到小排序
bash
[root@server ~ 21:42:08]# cat sort3.sh
#!/bin/bash
a=30
b=10
c=20
# 第 1 步:若 a<b 则交换,让 a 始终不小于 b
if [ $a -lt $b ];then
num=$b; b=$a; a=$num
fi
# 第 2 步:若 b<c 则交换,让 b 不小于 c(此时 c 成为最小值)
if [ $b -lt $c ];then
num=$c; c=$b; b=$num
fi
# 第 3 步:交换后 a、b 关系可能改变,再比较一次,保证 a 最大
if [ $a -lt $b ];then
num=$b; b=$a; a=$num
fi
echo "$a > $b > $c" # 输出最终排序结果
二、if 语句企业案例
2.1 监测 MySQL 服务是否正常
用端口是否监听判断服务状态,先确认命令行可用,再写成脚本。
bash
# 安装并启动 mariadb,关闭防火墙
[root@server ~ 21:43:24]# yum install -y mariadb-server
[root@server ~ 21:43:38]# systemctl enable mariadb --now
[root@server ~ 21:44:00]# systemctl disable firewalld.service --now
# 本地监测 3306 端口是否在监听(过滤到行数,便于数字比较)
[root@server ~ 21:45:30]# ss -lnt|grep ':3306'|wc -l
1
bash
[root@server ~ 21:47:21]# cat check_mysql.sh
#!/bin/bash
# 直接用 ss 命令做条件:grep -q 静默匹配到 3306 则退出码为 0
if ss -lnt|grep -q ':3306';then
echo "MySQL is Running."
else
echo "MySQL is Not Running."
fi
2.2 比较两个整数大小(带参数校验)
bash
[root@server ~ 21:49:17]# cat cmp_int.sh
#!/bin/bash
# 第 1 步:判断参数个数,必须正好传 2 个
if [ $# -ne 2 ];then
echo "Usage: $0 num1 num2"
exit 1
fi
a=$1 # 第 1 个参数
b=$2 # 第 2 个参数
# 第 2 步:用 expr 校验是否为整数(非整数的 expr 运算会返回非 0)
expr $a + 1 &>/dev/null
v1=$?
expr $b + 1 &>/dev/null
v2=$?
if [ $v1 -ne 0 -o $v2 -ne 0 ];then
echo "please provide two int number"
exit 2
fi
# 第 3 步:比较大小
if [ $a -lt $b ];then
echo "$a < $b"
elif [ $a -eq $b ];then
echo "$a = $b"
else
echo "$a > $b"
fi
2.3 判断字符串是否为数字(=~ 正则法)
bash
# [[ 支持 =~ 做正则匹配,^[0-9]+$ 表示纯数字
[root@server ~ 21:50:27]# [[ 123 =~ ^[0-9]+$ ]] && echo int || echo char
int
[root@server ~ 21:51:40]# [[ feiyun123 =~ ^[0-9]+$ ]] && echo int || echo char
char
2.4 开发 rsync 服务的启动脚本
rsync 是常用同步软件,下面用 if 语句实现类似 /etc/init.d/rsyncd {start|stop|restart|status} 的效果。
bash
# 环境准备:安装、以守护方式启动、确认 873 端口
[root@server ~ 21:53:07]# yum install -y rsync
[root@server ~ 21:53:32]# rsync --daemon
[root@server ~ 21:53:48]# ss -lnt|grep ':873'
LISTEN 0 5 *:873 *:*
bash
[root@server ~ 21:55:30]# cat /etc/init.d/rsyncd
#!/bin/bash
# 判断参数个数,必须传 1 个动作参数
if [ $# -ne 1 ];then
echo "Usage: $0 [ start | stop | restart | status ]"
exit 1
fi
if [ "$1" = "start" ];then # 启动分支
rsync --daemon
sleep 2
if ss -lnt|grep -q ':873';then # 再检测端口确认是否启动成功
echo "rsyncd is started."
exit 0
fi
elif [ "$1" = "stop" ];then # 停止分支
pkill rsync &>/dev/null
sleep 2
if ! ss -lnt|grep -q ':873';then
echo "rsyncd is stoped."
exit 0
fi
elif [ "$1" = "status" ];then # 状态分支
if ss -lnt|grep -q ':873';then
echo "rsyncd is started."
else
echo "rsyncd is stoped."
fi
elif [ "$1" = "restart" ];then # 重启分支
pkill rsync &>/dev/null
sleep 1
rsync --daemon
sleep 1
echo "rsyncd is restarted."
else # 未知参数
echo "Usage: $0 [ start | stop | restart | status ]"
exit 1
fi
bash
[root@server ~ 21:56:50]# bash /etc/init.d/rsyncd start
rsyncd is started.
[root@server ~ 21:57:54]# bash /etc/init.d/rsyncd stop
rsyncd is stoped.
三、Shell 函数
函数把重复代码封装成一段,起个名字,需要时直接调用。优势:减少代码量、易读易改、模块化。
3.1 函数的三种写法
bash
# 标准写法
function 函数名 () {
指令...
return n
}
# 简化写法 1:省略 ()
function 函数名 {
指令...
}
# 简化写法 2:省略 function
函数名 () {
指令...
}
3.2 执行要点
- 调用函数直接写函数名,不带
();函数必须先定义后调用。 - 外部函数文件需
source 文件名或. 文件名加载后使用。 - 函数内用
return退出函数并返回退出码,exit是退出整个脚本。 - 函数参数:
函数名 参数1 参数2,函数内用$1/$2/$#接收;$0仍是脚本名。
补充:位置参数 是脚本与函数共用的"传参通道"。
$0是脚本名,$1~$9是第 1~9 个参数(第 10 个及以后要写成${10}),$#是参数个数,$*与$@都代表全部参数(加双引号时"$@"会拆成一个个独立单词、"$*"会拼成一个整体字符串),$?是上一条命令的退出码。
3.3 关键示例
示例 1:hello 函数 + 先定义后调用的坑
bash
[root@server ~ 21:59:33]# cat fun1.sh
#!/bin/bash
function hello () { # 定义 hello 函数
echo "Hello World !"
}
hello # 调用函数
[root@server ~ 22:00:43]# bash fun1.sh
Hello World !
# 反例:先调用后定义会报「command not found」
[root@server ~ 22:02:33]# cat fun2.sh
#!/bin/bash
hello # 此时 hello 还没定义,报错
function hello () {
echo "Hello World !"
}
[root@server ~ 22:04:29]# bash fun2.sh
fun2.sh: line 2: hello: command not found
示例 2:带参数的函数(按参数输出不同颜色)
bash
[root@server ~ 22:05:32]# cat color.sh
#!/bin/bash
# 函数内用 $1 接收调用时传入的参数
function print () {
if [ "$1" == "PASS" ];then
echo -e '\033[1;32mPASS\033[0;39m' # 绿色
elif [ "$1" == "FAIL" ];then
echo -e '\033[1;31mFAIL\033[0;39m' # 红色
elif [ "$1" == "DONE" ];then
echo -e '\033[1;35mDONE\033[0;39m' # 紫色
else
echo "Usage: $0 PASS|FAIL|DONE"
fi
}
print $1 # 把脚本的第 1 个参数传给函数
[root@server ~ 22:06:54]# bash color.sh PASS
PASS
示例 3:函数的递归调用(求 1+2+...+n)
bash
[root@server ~ 22:07:45]# cat sum.sh
#!/bin/bash
function sum_out () { # 递归求和
if [ $1 -eq 1 ];then # 递归出口:n=1 时和为 1
sum=1
else # 否则 n + (n-1 的和),继续递归
sum=$[ $1 + $(sum_out $[ $1 - 1 ]) ]
fi
echo $sum
}
sum_out $1 # 以脚本参数作为 n
[root@server ~ 22:09:18]# bash sum.sh 10
55
递归经典警告------fork 炸弹 :
(){ :|:& };:会无限派生进程直至资源耗尽。实验中可用ulimit -u 100限制进程数防止死机,生产环境切勿执行。
3.4 企业案例:URL 检测脚本
bash
[root@server ~ 22:10:51]# cat check_url.sh
#!/bin/bash
function usage () { # 用法提示函数
echo "usage: $0 url"
exit 1
}
function check_url () { # 核心检测:用 wget 蜘蛛模式探测
wget --spider -q -o /dev/null --tries=1 -T 5 $1
[ $? -eq 0 ] && echo $1 is accessable || echo $1 is not accessable
}
function main () { # 入口函数,先校验参数
[ $# -ne 1 ] && usage
check_url $1
}
main $* # 把全部参数交给 main
[root@server ~ 22:11:42]# bash check_url.sh www.feiyun.com
www.feiyun.com is not accessable
四、case 条件语句
case 相当于多分支 if/elif/else,但结构更规范,常用于服务启动脚本(值较少且固定)。
4.1 语法
bash
case "变量" in
值1)
指令1
;; # 双分号结束该分支
值2 | 值3)
指令2
;; # 可用 | 匹配多个值
*) # 兜底分支,相当于 else
指令3
;;
esac
补充:case 的"值"不是精确相等比较,而是通配符匹配 ,支持
*(任意字符串)、?(任意单个字符)、[0-9](字符集合)等。正因如此,*)才能兜底匹配所有未命中的情况,等价于 else。
4.2 关键示例
示例 1:控制 sshd 服务(简化版)
bash
[root@server ~ 22:13:10]# cat sshd_case.sh
#!/bin/bash
case $1 in
start | stop | restart | reload | status) # 合法动作合并匹配
systemctl $1 sshd # 直接复用 $1 作为 action
;;
*)
echo "Usage: $0 start|stop|restart|reload|status"
;;
esac
示例 2:管理用户脚本 user-mgr
用 /etc/users 保存用户清单,支持 -a(添加)、-d(删除)、-s(查找)三个操作。
bash
[root@server ~ 22:14:06]# cat user-mgr
#!/bin/bash
# 必须以 root 运行(UID 非 0 则退出)
[ $UID -ne 0 ] && echo 'Please run as root' && exit 1
# 用户清单文件,不存在则创建
users_info_file=/etc/users
[ -f ${users_info_file} ] || touch ${users_info_file}
# 必须传 2 个参数:动作 + 用户名
if [ $# -ne 2 ];then
echo "Usage: user-mgr [ [-a|--add] | [-d|--del] | [-s|--search] ] username"
exit 2
fi
action=$1 # 动作
username=$2 # 用户名
case $action in
-s|--search) # 查找
if grep -q "username: $username" ${users_info_file};then
echo "$username is exist."
else
echo "$username is not exist."
fi
;;
-a|--add) # 添加
if grep -q "username: $username" ${users_info_file};then
echo "$username is exist."
else
chattr -i ${users_info_file} # 临时去掉只读属性
echo "username: $username" >> ${users_info_file}
chattr +i ${users_info_file} # 恢复不可修改属性,防止误删
echo "$username has been added."
fi
;;
-d|--del) # 删除
if grep -q "username: $username" ${users_info_file};then
chattr -i ${users_info_file}
sed -i "/username: $username/d" ${users_info_file} # 删除对应行
chattr +i ${users_info_file}
echo "$username has been deleted."
else
echo "$username is not exist."
fi
;;
*)
echo "Usage: user-mgr [ [-a|--add] | [-d|--del] | [-s|--search] ] username"
;;
esac
[root@server ~ 22:15:50]# bash user-mgr -a feiyun
Add User Success.
[root@server ~ 22:16:42]# bash user-mgr -s feiyun
feiyun is exist
五、while 和 until 循环
while 循环适合守护进程、无限循环 (需配合 sleep 控制频率);until 与 while 相反:条件不成立时执行、成立时退出。
5.1 语法
bash
while <条件表达式>
do
指令...
done
until <条件表达式>
do
指令...
done
补充:循环要避免死循环,通常包含三要素------初始化 (如
i=5赋初值)、条件判断 、更新 (如((i--))改变变量,让条件最终能变为假)。配合循环的三个流程控制:break跳出当前循环、continue跳过本次循环进入下一轮、exit直接结束整个脚本。
5.2 关键示例
示例 1:打印 5→1(while 与 until 对比)
bash
[root@server ~ 22:18:18]# cat loop1.sh
#!/bin/bash
i=5
while ((i>0)) # 条件成立时循环
do
echo $i
((i--)) # 自减,避免死循环
done
[root@server ~ 22:19:27]# cat loop2.sh
#!/bin/bash
i=5
until ((i==0)) # 条件不成立(i!=0)时循环
do
echo $i
((i--))
done
示例 2:求 1+2+...+100 的和
bash
[root@server ~ 22:21:16]# cat sum100.sh
#!/bin/bash
i=1
sum=0
while ((i<=100))
do
((sum+=i)) # 累加,等价于 sum=$[sum+i]
((i++)) # 自增,等价于 let i++
done
echo "1+2+...+100=$sum"
示例 3:猜数字(含输入校验)
bash
[root@server ~ 22:22:28]# cat guess.sh
#!/bin/bash
# 生成 1~49 随机数存到临时文件,供后续比较
random_num=$[ RANDOM % 49 + 1 ]
random_num_file=$(mktemp)
echo ${random_num} > ${random_num_file}
guess_count=0
while true # 无限循环,直到猜对才 exit
do
read -p "请输入一个1~49的数:" guess_num
if [[ ! $guess_num =~ ^[0-9]+$ ]]; then # 校验是否纯数字
echo "输入错误!请输入纯数字,本次不计入次数。"
elif (( guess_num < 1 || guess_num > 49 ));then # 校验范围
echo "输入错误!数字必须在1~49之间。"
else
let guess_count++ # 合法输入才计数
if (( guess_num < random_num ));then
echo "第 ${guess_count} 次:猜小了."
elif (( guess_num > random_num ));then
echo "第 ${guess_count} 次:猜大了."
else
echo "第 ${guess_count} 次:猜对了!"
rm -f ${random_num_file} # 猜对清理临时文件并退出
exit
fi
fi
done
5.3 后台运行常用命令
| 命令 | 作用 |
|---|---|
脚本 & |
放后台执行 |
Ctrl+C |
停止前台任务 |
Ctrl+Z |
暂停任务 |
bg / fg [编号] |
转后台 / 转前台 |
jobs |
查看任务 |
kill %编号 |
关闭指定任务 |
5.4 守护进程实战:后台检测并拉起 sshd
bash
[root@server ~ 22:23:49]# cat daemon_sshd.sh
#!/bin/bash
while true # 无限循环
do
systemctl is-active sshd.service &>/dev/null
if [ $? -ne 0 ];then # 服务未运行则重启
systemctl restart sshd.service &>/dev/null
fi
sleep 5 # 每 5 秒检测一次
done
# 后台运行此脚本:bash daemon_sshd.sh &
5.5 while 按行读文件的四种方式
bash
# 方式 1:exec 重定向标准输入
exec < /etc/hosts
while read line; do echo $line; done
# 方式 2:cat + 管道
cat /etc/hosts | while read line; do echo $line; done
# 方式 3:done 处重定向(推荐)
while read line; do echo $line; done < /etc/hosts
# 方式 4:IFS 指定分隔符为换行(配合 for)
IFS=$'\n'
for line in $(cat /etc/hosts); do echo $line; done
六、for 循环和 select 循环
6.1 for 的两种语法
bash
# 变量取值型(in 列表可省略,省略时等价于 in "$@")
for 变量名 in 取值列表
do
指令...
done
# C 语言型
for ((初始化; 条件; 增减));do
指令...
done
补充:两种 for 语法的选用------需要遍历已知的字符串列表、文件名、命令输出时用
for 变量 in 列表;需要精确控制循环次数和步长(如i+=2)时用 C 语言型for ((...))。break/continue同样适用于 for 循环。
6.2 for 基础示例
示例 1:遍历打印 1~5
bash
[root@server ~ 22:25:03]# cat for1.sh
#!/bin/bash
for i in {1..5} # 大括号展开 1~5
do
echo $i
done
[root@server ~ 22:26:19]# bash for1.sh
1
2
3
4
5
示例 2:求 1~10 的和
bash
[root@server ~ 22:27:27]# cat for_sum.sh
#!/bin/bash
sum=0
for i in {1..10}
do
sum=$[ sum + i ] # 累加
done
echo $(seq -s '+' 10)=$sum
示例 3:求 100~200 之间的素数(嵌套循环 + break)
bash
[root@server ~ 22:28:46]# cat prime.sh
#!/bin/bash
echo -n "100~200之间的素数为:"
for ((i=100;i<=200;i++))
do
for ((j=2;j<=i-1;j++)) # 除数范围 2 ~ i-1
do
if [ $[i%j] -eq 0 ];then # 能被整除,说明不是素数
break # 提前跳出内层 j 循环
fi
if [ $j -eq $[i-1] ];then # 除到最后都没整除,则是素数
echo -n " $i"
fi
done
done
echo
示例 4:九九乘法表(嵌套循环 + echo -en 控制不换行)
bash
[root@server ~ 22:30:46]# cat 99.sh
#!/bin/bash
for num1 in {1..9}
do
for ((num2=1;num2<=num1;num2++))
do
echo -en "$num2*$num1=$[ num2 * num1 ]\t" # -n 不换行,\t 制表符对齐
done
echo # 每行打印完换行
done
示例 5:百钱买百鸡(三层嵌套经典题)
鸡翁 5 钱/只、鸡母 3 钱/只、鸡雏 3 只 1 钱,100 钱正好买 100 只,问各几何。
bash
[root@server ~ 22:31:38]# cat chicken.sh
#!/bin/bash
sum=100 # 总只数
money=100 # 总钱数
for ((cock=1;cock<=money/5;cock++)) # 鸡翁最多 20 只
do
for ((hen=1;hen<=money/3;hen++)) # 鸡母最多 33 只
do
for chick in {3..100..3} # 鸡雏只能 3 的倍数
do
# 同时满足总数=100、钱数=100 才输出
if [ $[cock+hen+chick] -eq $sum -a $[cock*5+hen*3+chick/3] -eq $money ];then
echo "鸡翁:$cock 鸡母:$hen 鸡雏:$chick"
fi
done
done
done
6.3 for 企业案例
示例 1:批量修改文件扩展名(txt → jpg)
bash
[root@server ~ 22:32:32]# touch file-{1..3}.txt
[root@server ~ 22:32:51]# cat rename.sh
#!/bin/bash
for file in $(ls *.txt) # 遍历所有 .txt 文件
do
new_name=$(echo $file|sed 's/.txt$/.jpg/') # sed 替换后缀为新名
mv $file ${new_name} # 重命名
done
示例 2:批量创建 MySQL 库并备份
bash
[root@server ~ 22:33:41]# cat create_dbs.sh
#!/bin/bash
MYSQL_CMD='mysql -uroot -e '
for db in web1 web2 # 遍历库名
do
${MYSQL_CMD} "create database $db;" # 逐个建库
done
[root@server ~ 22:35:00]# cat dump_dbs.sh
#!/bin/bash
MYSQL_CMD='mysqldump -uroot '
for db in web1 web2
do
${MYSQL_CMD} $db > $db.sql # 逐个备份到同名 .sql
done
示例 3:批量创建系统账号(随机密码 + 首次登录强制改密)
bash
[root@server ~ 22:36:33]# cat create_users.sh
#!/bin/bash
. /etc/init.d/functions # 引入 action 等友好输出函数
users_info_file=/etc/users # 保存账号密码清单
users_list_file=/tmp/users # 待创建用户名清单(每行一个)
[ ! -f $users_info_file ] && touch $users_info_file
exec < ${users_list_file} # 把清单文件作为标准输入
while read username # 逐行读取用户名
do
# 用「用户名+随机数」md5 后截取 8 位作为密码
pass=$(echo "$username+$RANDOM" | md5sum | cut -c 3-10)
# 建用户、强制首登改密、设置密码,全部成功才记入清单
useradd $username &>/dev/null && chage -d 0 $username && \
echo "$pass" | passwd --stdin $username &>/dev/null && \
echo -e "username: $username \t password:$pass" >> $users_info_file
if [ $? -eq 0 ];then # 根据返回值给出成功/失败提示
action "$username is ok" /bin/true
else
action "$username is fail" /bin/false
fi
done
6.4 Linux 生成随机数的常用方法
bash
# 1. $RANDOM(0~32767,可 md5 增强)
echo "feiyun$RANDOM" | md5sum | cut -c 1-8
# 2. openssl
openssl rand -base64 8
# 3. date 纳秒
date +%s%N
# 4. /dev/urandom
head /dev/urandom | md5sum
# 5. UUID
cat /proc/sys/kernel/random/uuid
# 6. mkpasswd(expect 提供,可控制长度/数字/大小写/符号)
mkpasswd -l 20 -d 4 -c 4 -C 4 -s 4
6.5 select 循环:生成交互菜单
select 会列出菜单项并等到用户输入对应序号,常配合 case 使用。
bash
[root@server ~ 22:37:28]# cat select_menu.sh
#!/bin/bash
PS3="请选择要安装的软件: " # PS3 自定义选择提示符
select app in "Install httpd" "Install mysql" "Exit"
do
echo "你选择了第 $REPLY 个条目。" # REPLY 是本次选择对应的序号
case $app in
"Install httpd")
yum install -y httpd && echo "httpd DONE"
;;
"Install mysql")
yum install -y mariadb-server && echo "mysql DONE"
;;
"Exit")
exit # 选择退出才结束循环
;;
esac
done
bash
[root@server ~ 22:38:42]# bash select_menu.sh
1) Install httpd
2) Install mysql
3) Exit
请选择要安装的软件: 1
你选择了第 1 个条目。