十、Linux Shell脚本:流程控制语句

简介: 要让脚本从简单的“指令清单”升级为能干的“小助手”,就需要用if教它根据条件做判断,用for和while循环让它重复处理任务,再用case语句帮它在多个选项中做出清晰的选择
在掌握了Shell脚本的 变量与运算之后, 流程控制构建复杂和实用脚本关键。它 允许脚本根据 不同的条件选择执行路径,或 重复执行特定任务,从而 实现脚本灵活性与自动化

## 思维导图
image.png
image.png
image.png


## 一、条件判断
if 语句是 最基本条件控制结构,它 评估一个命令退出状态码 (exit code)。如果 退出码为 0 (成功),则 条件为真;如果为 非 0 (失败),则 条件为假

### if 的基本结构
格式:
bash if [ 条件判断 ]; then # 条件为真时执行的代码块 fi
代码示例:检查文件是否存在
bash #!/bin/bash TARGET_FILE="/etc/hosts" if [ -f "$TARGET_FILE" ]; then echo "文件 '$TARGET_FILE' 存在。" fi

### if...else 结构
格式:
bash if [ 条件判断 ]; then # 条件为真时执行的代码块 else # 条件为假时执行的代码块 fi
代码示例:判断目录是否存在
bash #!/bin/bash TARGET_DIR="/var/log/non_existent_dir" if [ -d "$TARGET_DIR" ]; then echo "目录 '$TARGET_DIR' 存在。" else echo "目录 '$TARGET_DIR' 不存在,将尝试创建。" mkdir -p "$TARGET_DIR" fi

### if...elif...else 结构
格式:
bash if [ 条件1 ]; then # 条件1为真时执行 elif [ 条件2 ]; then # 条件1为假,但条件2为真时执行 else # 以上所有条件都为假时执行 fi
代码示例:根据HTTP状态码判断响应
bash #!/bin/bash HTTP_CODE=200 if [ $HTTP_CODE -eq 200 ]; then echo "请求成功 (OK)" elif [ $HTTP_CODE -eq 404 ]; then echo "资源未找到 (Not Found)" elif [ $HTTP_CODE -eq 500 ]; then echo "服务器内部错误 (Internal Server Error)" else echo "收到未知的HTTP状态码: $HTTP_CODE" fi

### 条件判断的实现:test 和 [ ]
在Shell中, if 后的条件通常由 test 命令或其等价形式 [ ... ] 来实现。 [[ ... ]][ ... ]扩展版本,提供了 更多功能 (如模式匹配、逻辑与/或)。

常见判断类型:
> 文件测试: -f (是普通文件?), -d (是目录?), -e (存在?), -s (大小非0?), -r (可读?), -w (可写?), -x (可执行?)
字符串比较: "$str1" = "$str2", "$str1" != "$str2", -z "$str" (字符串为空?), -n "$str" (字符串非空?)
整数比较: -eq (等于), -ne (不等于), -gt (大于), -ge (大于等于), -lt (小于), -le (小于等于)

## 二、循环结构
循环用于 重复执行一段代码,直到 满足某个退出条件。

### for 循环
for 循环擅长 遍历一个列表 (字符串、文件名、数字序列等) 或进行 C语言风格数值循环

格式 (遍历列表):
bash for variable_name in item1 item2 item3 ...; do # 循环体 done
代码示例 (遍历并重命名文件):
bash #!/bin/bash # 将所有 .txt 文件重命名为 .txt.bak for filename in *.txt; do if [ -f "$filename" ]; then echo "正在备份: $filename -> ${filename}.bak" mv "$filename" "${filename}.bak" fi done

格式 (C风格数值循环):
bash for (( initialization; condition; step )); do # 循环体 done
代码示例 (执行三次ping测试):
bash #!/bin/bash TARGET_HOST="8.8.8.8" for (( i=1; i<=3; i++ )); do echo "--- 第 $i 次 PING 测试 ---" ping -c 1 "$TARGET_HOST" done

### while 循环
while 循环在 每次迭代前检查条件,只要 条件为真,就 继续执行循环体。

格式:
bash while [ 条件判断 ]; do # 循环体 done
代码示例:逐行读取文件
bash #!/bin/bash CONFIG_FILE="/etc/fstab" while read -r line; do # 忽略注释和空行 if [[ "$line" =~ ^# || -z "$line" ]]; then continue fi echo "读取到配置行: $line" done < "$CONFIG_FILE"

### until 循环
until 循环与 while 逻辑相反:只要 条件为假,就 继续执行循环体,直到 条件变为真才停止。

格式:
bash until [ 条件判断 ]; do # 循环体 done
代码示例:等待服务端口启动
bash #!/bin/bash PORT=8080 TIMEOUT=10 COUNT=0 until nc -z localhost $PORT >/dev/null 2>&1; do if [ $COUNT -ge $TIMEOUT ]; then echo "等待端口 $PORT 超时!" exit 1 fi echo "端口 $PORT 尚未启动,等待1秒..." sleep 1 COUNT=$((COUNT + 1)) done echo "端口 $PORT 已成功启动!"

### 循环控制:break 和 continue
break: 立即当前循环中完全跳出 continue: 跳过当前循环的 剩余部分,直接 开始下一次迭代。

代码示例:在循环中处理文件
bash #!/bin/bash for file in /var/log/*; do if [ -d "$file" ]; then continue # 如果是目录,则跳过 fi echo "正在处理文件: $file" if [ -s "$file" ] && grep -q "ERROR" "$file"; then echo "在文件 '$file' 中找到错误,停止处理。" break # 找到错误后,完全停止 fi done

## 三、分支选择
case 语句提供了一种 更清晰的方式来 处理多重条件分支,是 if...elif...else一种替代方案,特别适合 基于单个变量值进行匹配

格式:
bash case $variable in pattern1) # 匹配 pattern1 时执行 ;; pattern2|pattern3) # 匹配 pattern2 或 pattern3 时执行 ;; *) # 默认情况,当以上模式都不匹配时执行 ;; esac
代码示例:脚本参数解析
bash #!/bin/bash ACTION=$1 case $ACTION in start) echo "正在启动服务..." # systemctl start my_service ;; stop) echo "正在停止服务..." # systemctl stop my_service ;; status) echo "检查服务状态..." # systemctl status my_service ;; *) echo "用法: $0 {start|stop|status}" exit 1 ;; esac

---
## 练习题

题目:

  1. 文件权限检查:写一个脚本,接收一个文件名作为参数 ($1)。脚本需要判断当前用户对该文件是否同时拥有读、写、执行权限。如果同时拥有,打印 "Full permissions granted";否则打印 "Permissions incomplete"。
  2. 字符串与逻辑判断:写一个脚本,检查变量 ENVIRONMENT 的值。如果值是 production 并且 变量 FORCE_DEPLOY 的值不是 true,则打印 "Safety check passed: Not a forced production deploy." 并退出;否则,打印 "Proceeding with deployment."。
  3. C风格 for 循环与算术:使用C风格的 for 循环,打印出从10到20之间所有的偶数 (包括10和20)。
  4. for 循环与通配符:写一个脚本,查找 /var/log 目录下所有以 .log 结尾的非空文件,并打印出它们的文件名。
  5. while 循环读取标准输入:写一个脚本,持续读取用户从键盘输入的内容,直到用户输入 quit 为止。对于quit 的输入,脚本应该将其回显到屏幕上。
  6. until 循环与命令退出码grep 命令在找到匹配项时退出码为0,找不到时为1。写一个 until 循环,每隔2秒检查一次系统日志 (/var/log/messagesjournalctl -f 的输出,为简化可检查一个普通文件) 是否出现了 "critical error" 字符串,一旦出现就打印 "Critical error detected!" 并退出。
  7. 嵌套循环与 break n:写一个嵌套循环。外层循环从1到3,内层循环从1到3。在内层循环中,如果内外两个循环变量 (ij) 相等,则同时跳出内外两层循环。每次循环都打印当前的 ij 的值。
  8. case 语句与通配符:写一个 case 语句,判断一个文件名变量 FILENAME文件类型。如果文件名以 .log 结尾,打印 "Log file";如果以 .tar.gz.tgz 结尾,打印 "Compressed archive";如果以 .sh 结尾,打印 "Shell script";其他情况打印 "Unknown file type"。
  9. select 菜单 (高级)select 是一个特殊的循环结构,用于创建交互式菜单。写一个脚本,使用 select 让用户从 "Start", "Stop", "Restart", "Exit" 四个选项中选择一个操作,并根据用户的选择打印相应的信息。当用户选择 "Exit" 时,脚本退出。
答案与解析:
  1. 文件权限检查:
    ```bash

    !/bin/bash

    if [ -z "$1" ]; then
    echo "用法: $0 <文件名>"
    exit 1
    fi

if [ -r "$1" ] && [ -w "$1" ] && [ -x "$1" ]; then
echo "Full permissions granted"
else
echo "Permissions incomplete"
fi

   *   **解析:** `if` 语句中的 `-r`, `-w`, `-x` 是文件测试操作符,分别检查读、写、执行权限。`&&` 是逻辑与操作符,要求<font color="teal">所有条件都为真</font>才执行 `then` 块。

2.  **字符串与逻辑判断:**
```bash
#!/bin/bash
ENVIRONMENT="production"
FORCE_DEPLOY="false"

if [[ "$ENVIRONMENT" == "production" && "$FORCE_DEPLOY" != "true" ]]; then
  echo "Safety check passed: Not a forced production deploy."
  exit 0
else
  echo "Proceeding with deployment."
fi
  • 解析: 使用了 [[ ... ]] 扩展测试,它内部支持 && (逻辑与) 和 != (字符串不等于) 操作符,语法更自然
  1. C风格 for 循环与算术:

    #!/bin/bash
    for (( num=10; num<=20; num+=2 )); do
    echo $num
    done
    
    • 解析: C风格的 for 循环通过初始化 num=10条件 num<=20,以及步进 num+=2 来精确控制循环,直接打印出范围内的偶数。
  2. for 循环与通配符:

    #!/bin/bash
    for logfile in /var/log/*.log; do
    if [ -s "$logfile" ]; then
    echo "找到非空日志文件: $(basename "$logfile")"
    fi
    done
    
    • 解析: *.log 是一个通配符for 循环会遍历所有匹配的文件名。-s 文件测试操作符用于判断文件大小是否大于零basename 命令用于提取文件名,去除路径。
  3. while 循环读取标准输入:

    #!/bin/bash
    echo "请输入内容 (输入 'quit' 退出):"
    while read -r input_line; do
    if [ "$input_line" == "quit" ]; then
    break
    fi
    echo "你输入了: $input_line"
    done
    
    • 解析: while read -r input_line读取标准输入标准模式。循环会一直持续,直到 read 命令失败 (例如,用户按下Ctrl+D) 或遇到 break
  4. until 循环与命令退出码:
    ```bash

    !/bin/bash

    LOG_FILE_TO_CHECK="my_app.log"
    touch $LOG_FILE_TO_CHECK # 创建一个空文件用于测试

echo "正在监控 '$LOG_FILE_TO_CHECK' ..."

在另一个终端执行 echo "critical error" >> my_app.log 来触发

until grep -q "critical error" "$LOG_FILE_TO_CHECK"; do
sleep 2
done

echo "Critical error detected!"

   *   **解析:** `until` 循环的<font color="darkslategray">条件是命令本身</font> (`grep -q ...`)。只要 `grep` <font color="indigo">找不到</font>字符串 (退出码非0,条件为假),循环就<font color="blue">继续</font>。一旦<font color="red">找到</font> (退出码为0,条件为真),循环<font color="green">终止</font>。

7.  **嵌套循环与 `break n`:**
```bash
#!/bin/bash
for (( i=1; i<=3; i++ )); do
  echo "外层循环: i=$i"
  for (( j=1; j<=3; j++ )); do
    echo "  内层循环: j=$j"
    if [ $i -eq $j ]; then
      echo "  i 等于 j,跳出所有循环!"
      break 2 # '2' 表示跳出两层循环
    fi
  done
done
  • 解析: break n 命令可以跳出指定层数的循环。break 1 (或 break) 只跳出当前层,break 2 跳出当前层和其外一层。
  1. case 语句与通配符:
    ```bash

    !/bin/bash

    FILENAME="archive-2023.tar.gz"

case $FILENAME in
.log)
echo "Log file"
;;
.tar.gz|.tgz)
echo "Compressed archive"
;;
.sh)
echo "Shell script"
;;
*)
echo "Unknown file type"
;;
esac

   *   **解析:** `case` 语句的模式<font color="purple">支持通配符</font>,如 `*` (匹配任意字符序列)。`|` 用于<font color="teal">分隔多个模式</font>,表示“或”。

9.  **`select` 菜单:**
```bash
#!/bin/bash
PS3="请选择一个操作 (输入数字): "
options=("Start" "Stop" "Restart" "Exit")

select opt in "${options[@]}"; do
  case $opt in
    "Start")
      echo "正在启动..."
      ;;
    "Stop")
      echo "正在停止..."
      ;;
    "Restart")
      echo "正在重启..."
      ;;
    "Exit")
      echo "退出脚本。"
      break
      ;;
    *)
      echo "无效选项 '$REPLY',请重新选择。"
      ;;
  esac
done
  • 解析: select自动生成一个带编号的菜单。用户的输入编号被翻译对应的选项值 (赋给变量opt),而原始输入则保存在 $REPLY 中。
目录
相关文章
|
5月前
|
Shell Linux
九、Linux Shell脚本:运算符与表达式
Shell脚本里的变量就像一个个贴着标签的“箱子”。装东西(赋值)时,=两边千万不能有空格。用单引号''装进去的东西会原封不动,用双引号""则会让里面的$变量先“变身”再装箱。默认箱子只能在当前“房间”(Shell进程)用,想让隔壁房间(子进程)也能看到,就得给箱子盖个export的“出口”戳。此外,Shell还自带了$?(上条命令的成绩单)和$1(别人递进来的第一个包裹)等许多特殊箱子,非常有用。
426 1
|
5月前
|
Linux 应用服务中间件 Shell
二、Linux文本处理与文件操作核心命令
熟悉了Linux的基本“行走”后,就该拿起真正的“工具”干活了。用grep这个“放大镜”在文件里搜索内容,用find这个“探测器”在系统中寻找文件,再用tar把东西打包带走。最关键的是要学会使用管道符|,它像一条流水线,能把这些命令串联起来,让简单工具组合出强大的功能,比如 ps -ef | grep 'nginx' 就能快速找出nginx进程。
594 1
二、Linux文本处理与文件操作核心命令
|
5月前
|
消息中间件 人工智能 运维
事件驱动重塑 AI 数据链路:阿里云 EventBridge 发布 AI ETL 新范式
“一个简单的数据集成任务,开始时总是轻松愉快的,但随着业务扩展,数据源越来越多,格式越来越乱,整个数据链路就会变得一团糟。”陈涛在演讲中指出了当前 AI 数据处理的普遍困境。扩展难、运维难、稳定性差,这三大挑战已成为制约 AI 应用创新和落地的关键瓶颈。针对这些痛点,在2025云栖大会期间,阿里云重磅发布了事件驱动 AI ETL 新范式,其核心产品 EventBridge 通过深度集成 AI 能力,为开发者提供了一套革命性的解决方案,旨在彻底改变 AI 时代的数据准备与处理方式。
505 41
|
Web App开发 弹性计算 编解码
最佳实践:如何扩展你的SRS并发能力?
当我们用SRS快速搭建了视频服务,业务也开始上线运行了,很快就会遇到一个问题:如何支持更多的人观看?如何支持更多的人推流?这本质上就是系统的水平扩展能力,SRS当然是支持的,而且有多种扩展的方法,这篇文章就就详细分析各种扩展的方案,以及各种方案的应用场景和优缺点。
3273 0
最佳实践:如何扩展你的SRS并发能力?
|
5月前
|
安全 Linux Shell
四、Linux核心工具:Vim, 文件链接与SSH
要想在Linux世界里游刃有余,光会“走路”还不够,还得配上几样“高级装备”。首先是Vim编辑器,它像一把瑞士军刀,让你能在命令行里高效地修改文件。然后要懂“软硬链接”,软链接像个快捷方式,硬链接则是给文件起了个别名。最后,SSH是你的“传送门”,不仅能让你安全地远程登录服务器,还能用scp轻松传输文件,设置好密钥更能实现免-密登录,极大提升效率。
432 4
|
5月前
|
存储 Shell Linux
八、Linux Shell 脚本:变量与字符串
Shell脚本里的变量就像一个个贴着标签的“箱子”。装东西(赋值)时,=两边千万不能有空格。用单引号''装进去的东西会原封不动,用双引号""则会让里面的$变量先“变身”再装箱。默认箱子只能在当前“房间”(Shell进程)用,想让隔壁房间(子进程)也能看到,就得给箱子盖个export的“出口”戳。此外,Shell还自带了$?(上条命令的成绩单)和$1(别人递进来的第一个包裹)等许多特殊箱子,非常有用。
496 3
|
5月前
|
存储 安全 Linux
三、Linux用户与权限管理详解
管理Linux系统就像当一个大楼的管家。首先,你得用useradd和passwd给新员工发“钥匙”(创建用户并设密码),并用groupadd把他们分到不同“部门”(用户组)。然后,你要为每个“房间”(文件或目录)设定规矩,这就是文件权限:用chmod命令设置谁(所有者、同部门、其他人)可以“进入”(x)、“读取”(r)或“写入”(w)。最后,用chown还能把房间的归属权转让给别人。
637 3
|
5月前
|
Linux Shell 数据处理
十三、Linux Shell脚本:文本处理三剑客之 sed 与 awk
如果说grep是文本处理的“探照灯”,那sed和awk就是“手术刀”和“瑞士军刀”。sed 擅长按行“动手术”,可以轻松地删除、打印或替换特定行的内容,尤其是它的替换功能(s/.../.../)非常强大。而 awk 更厉害,它天生就能看懂按列(字段)分布的数据,像个小型的Excel。你可以让它只打印某几列,或者根据某一列的值做计算和判断,特别适合生成统计报告。
509 1
|
5月前
|
存储 安全 Unix
七、Linux Shell 与脚本基础
别再一遍遍地敲重复的命令了,把它们写进Shell脚本,就能一键搞定。脚本本质上就是个存着一堆命令的文本文件,但要让它“活”起来,有几个关键点:文件开头最好用#!/usr/bin/env bash来指定解释器,并用chmod +x给它执行权限。执行时也有讲究:./script.sh是在一个新“房间”(子Shell)里跑,不影响你;而source script.sh是在当前“房间”里跑,适合用来加载环境变量和配置文件。
517 9
|
5月前
|
安全 Ubuntu Unix
一、初识 Linux 与基本命令
玩转Linux命令行,就像探索一座新城市。首先要熟悉它的“地图”,也就是/根目录下/etc(放配置)、/home(住家)这些核心区域。然后掌握几个“生存口令”:用ls看周围,cd去别处,mkdir建新房,cp/mv搬东西,再用cat或tail看文件内容。最后,别忘了随时按Tab键,它能帮你自动补全命令和路径,是提高效率的第一神器。
892 57