Shell 脚本编程:条件判断与循环

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 个条目。
相关推荐
Peregrine92 小时前
Linux 基本指令大全
linux·运维·服务器
Source.Liu2 小时前
【A11】拆分前端,实现多页面方案
前端·rust
拂拉氏2 小时前
【知识讲解】 Linux动静态链接认识
linux·动静态链接
涛涛ing2 小时前
Navigation API正式Baseline:那个让无数前端头疼的History API,终于可以退休了
前端
Hilaku3 小时前
为什么我劝应届生不要去创业公司写前端?
前端·javascript·程序员
郑州光合科技余经理3 小时前
出海外卖平台:支付对接为什么要和下单收款分开
android·java·开发语言·前端·后端·php·ai编程
做运维的阿瑞3 小时前
利用GitHub配置镜像加速,解决 Docker镜像拉取问题
运维·docker·容器·github
Huangjin007_3 小时前
【Linux 系统篇(二十八)】文件(五):Ext 系列文件系统(下):Ext2块组结构,inode与文件寻址原理
linux·运维·服务器
data analyse 4563 小时前
前端自建访问统计,访客标识怎么设计才稳?Cookie、本地存储与设备指纹的取舍
前端·数据分析