Shell 流程控制(手把手讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

在 Shell 脚本编程中,流程控制是实现自动化任务的核心工具。它允许开发者通过条件判断、循环结构和函数调用等方式,让脚本根据实际情况动态调整执行路径。无论是检查文件是否存在、批量处理数据,还是构建复杂的系统管理工具,掌握 Shell 流程控制都是提升脚本效率和灵活性的关键。本文将从基础语法到实战案例,逐步解析这一主题,帮助读者建立系统化的理解。


一、流程控制的基础语法

1.1 条件判断:分支逻辑的起点

Shell 脚本的条件判断主要通过 if 语句实现,其核心逻辑类似于生活中的“如果…那么…”决策模式。例如,判断一个文件是否存在,若存在则输出提示信息,否则执行其他操作。

if [ -f "/path/to/file.txt" ]; then  
  echo "文件存在,准备处理..."  
else  
  echo "文件不存在,请检查路径!"  
fi  

这里 [ ]test 命令的简写形式,用于执行条件表达式。常见的测试运算符包括:

  • -f:检查文件是否为普通文件
  • -d:检查是否为目录
  • -n:判断字符串长度是否非零
  • ==:比较字符串是否相等

比喻:可以把 if 语句想象成交通信号灯——当条件(红灯)满足时,执行对应的操作(停车);否则继续执行其他逻辑(绿灯通行)。

1.2 简化条件表达式:[[ ]] 的优势

在较新的 Shell 版本(如 Bash 4.x+)中,[[ ]] 替代了 [ ],提供了更强大的功能:

  • 支持通配符(如 [[ $var == *.txt ]]
  • 自动忽略空格(无需转义)
  • 支持逻辑运算符 &&||
if [[ -f "/path/to/file.txt" && -n "$VAR" ]]; then  
  echo "文件存在且变量非空,继续执行!"  
fi  

二、条件判断的进阶:多分支与模式匹配

2.1 elif 的分层决策

当需要处理多个条件分支时,elif(else if 的缩写)可以构建阶梯式判断逻辑。例如,根据用户输入的不同数值执行不同操作:

read -p "请输入一个数字(1-3):" num  

if [[ $num -eq 1 ]]; then  
  echo "你选择了选项1"  
elif [[ $num -eq 2 ]]; then  
  echo "你选择了选项2"  
elif [[ $num -eq 3 ]]; then  
  echo "你选择了选项3"  
else  
  echo "输入无效,请重新运行脚本!"  
fi  

2.2 case 语句:模式匹配的利器

对于字符串匹配场景(如菜单选择),case 语句提供了更简洁的语法:

read -p "请选择操作(start/stop/restart):" action  

case $action in  
  start)  
    echo "正在启动服务..."  
    ;;  
  stop)  
    echo "正在停止服务..."  
    ;;  
  restart)  
    echo "正在重启服务..."  
    ;;  
  *)  
    echo "无效的操作,请重新输入!"  
    ;;  
esac  

比喻case 语句就像一个自动分拣机——根据输入的“形状”(模式),将数据导向对应的处理通道。


三、循环结构:自动化任务的核心

3.1 for 循环:已知次数的重复操作

for 循环适用于已知迭代次数的场景,如遍历文件列表或数字序列:

示例1:遍历数字序列

for i in {1..5}; do  
  echo "当前计数:$i"  
done  

示例2:处理指定目录下的所有 .log 文件

for file in /var/log/*.log; do  
  echo "正在压缩文件:$file"  
  gzip "$file"  
done  

3.2 while 循环:条件满足时持续执行

while 循环在条件为真时重复执行代码块,适合处理不确定次数的任务:

count=0  
while [ $count -lt 3 ]; do  
  echo "当前计数:$count"  
  ((count++))  
done  

关键点:必须确保循环有终止条件,否则可能导致无限循环。

3.3 until 循环:反向条件的控制

until 循环在条件为假时才执行,逻辑上等同于 while ! condition

until [ $count -eq 3 ]; do  
  echo "当前计数:$count"  
  ((count++))  
done  

3.4 循环控制语句:breakcontinue

  • break:立即退出当前循环
  • continue:跳过本次循环剩余代码,进入下一次迭代
for num in 1 2 3 4 5; do  
  if [[ $num -eq 3 ]]; then  
    continue  # 跳过数值3  
  fi  
  echo "当前数值:$num"  
  if [[ $num -eq 5 ]]; then  
    break     # 在数值5时终止循环  
  fi  
done  

四、函数与流程控制的结合

4.1 封装逻辑:函数的定义与调用

通过函数将重复的流程控制逻辑封装,可以提升代码复用性:

check_file() {  
  local file="$1"  
  if [[ -f "$file" ]]; then  
    echo "文件 $file 存在"  
  else  
    echo "文件 $file 不存在"  
    exit 1  
  fi  
}  

check_file "/etc/hosts"  

4.2 错误处理:trap 命令的使用

结合 trap 可以捕获信号并执行清理操作,例如在脚本异常退出时删除临时文件:

cleanup() {  
  rm -f /tmp/tempfile  
  echo "临时文件已清理"  
}  

trap cleanup EXIT  # 在脚本退出时执行 cleanup 函数  

echo "正在运行任务..."  
sleep 5  

五、实战案例:构建一个备份脚本

5.1 需求分析

  • 每日备份指定目录到 /backup
  • 若备份目录不存在,则自动创建
  • 记录备份日志到 /var/log/backup.log

5.2 脚本实现

#!/bin/bash  

BACKUP_DIR="/backup"  
SOURCE_DIR="/var/www"  
LOG_FILE="/var/log/backup.log"  

if [[ ! -d "$BACKUP_DIR" ]]; then  
  echo "创建备份目录:$BACKUP_DIR"  
  mkdir -p "$BACKUP_DIR" || exit 1  # 创建失败则退出  
fi  

TIMESTAMP=$(date +%Y%m%d-%H%M%S)  
ZIP_FILE="$BACKUP_DIR/backup-$TIMESTAMP.zip"  

echo "开始备份:$(date)" >> "$LOG_FILE"  
zip -qr "$ZIP_FILE" "$SOURCE_DIR" >> "$LOG_FILE" 2>&1  

if [[ $? -eq 0 ]]; then  
  echo "备份成功:$ZIP_FILE"  
else  
  echo "备份失败,请检查日志文件!"  
  exit 1  
fi  

5.3 关键点解析

  • 使用 mkdir -p 自动创建多级目录
  • zip 命令的输出重定向到日志文件
  • 通过 $? 检查上一条命令的退出状态

六、进阶技巧与常见问题

6.1 条件表达式中的空格陷阱

[ ] 中,条件两边必须有空格,否则会引发语法错误:

if [ -f$file ]; then ...  

if [ -f "$file" ]; then ...  

6.2 环境变量与参数传递

在脚本中,可以通过 $1$2 等引用命令行参数,结合条件判断实现动态行为:

ACTION=$1  

case $ACTION in  
  start)  
    echo "启动服务"  
    ;;  
  stop)  
    echo "停止服务"  
    ;;  
  *)  
    echo "用法:$0 {start|stop}"  
    exit 1  
    ;;  
esac  

6.3 脚本调试技巧

使用 -x 参数启用调试模式,跟踪脚本执行过程:

bash -x backup_script.sh  

结论

Shell 流程控制是构建高效自动化脚本的核心能力。通过条件判断、循环结构和函数封装,开发者可以灵活应对文件操作、系统监控、批量任务等场景。本文通过基础语法、案例解析和实战脚本,系统性地展示了这一主题的实现方法。建议读者通过编写实际脚本(如日志清理工具、定时任务调度器)来巩固知识,并逐步探索 select 语句、read 命令等高级特性。掌握流程控制后,Shell 脚本将从简单的命令组合,升级为可扩展、可维护的系统管理工具。

最新发布