当前位置:

首页 > 编程开发 > Linux Shell脚本之流程控制语句举例详解

Linux Shell脚本之流程控制语句举例详解

Shell脚本流程控制包含:if/elif/else条件判断实现分支执行;for/while/until循环实现重复任务;case分支选择处理多条件匹配;而break/continue则分别用于跳出当前循环和提前进入下一次循环。这些控制结构共同构成脚本的逻辑基础,使自动化任务灵活高效。

在掌握了Shell脚本的变量与运算之后,流程控制就成了构建复杂脚本的关键环节。它让脚本能够根据不同的条件自行选择执行路径,或者反复执行某个任务,从而实现真正的灵活和自动化。说白了,没有流程控制,脚本就是一条路走到黑,有了它,才能像人一样“判断”和“循环”。

思维导图

Linux Shell脚本之流程控制语句举例详解

Linux Shell脚本之流程控制语句举例详解

Linux Shell脚本之流程控制语句举例详解

一、条件判断

if 语句是条件控制里最基础、最常用的结构。它的核心逻辑很简单:评估一个命令的退出状态码。如果退出码是 0(表示成功),条件就算成立;如果非 0(表示失败),条件就不成立。

if 的基本结构

格式:

if [ 条件判断 ]; then
  # 条件为真时执行的代码块
fi

代码示例:检查文件是否存在

#!/bin/bash
TARGET_FILE="/etc/hosts"

if [ -f "$TARGET_FILE" ]; then
  echo "文件 '$TARGET_FILE' 存在。"
fi

if...else 结构

格式:

if [ 条件判断 ]; then
  # 条件为真时执行的代码块
else
  # 条件为假时执行的代码块
fi

代码示例:判断目录是否存在

#!/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 结构

格式:

if [ 条件1 ]; then
  # 条件1为真时执行
elif [ 条件2 ]; then
  # 条件1为假,但条件2为真时执行
else
  # 以上所有条件都为假时执行
fi

代码示例:根据HTTP状态码判断响应

#!/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(小于等于)

二、循环结构

循环用来重复执行一段代码,直到满足某个退出条件为止。Shell提供了几种循环方式,各有各的适用场景。

for 循环

for 循环最擅长遍历一个列表——可以是字符串、文件名、数字序列等,也可以写C语言风格的数值循环。

格式(遍历列表):

for variable_name in item1 item2 item3 ...; do
  # 循环体
done

代码示例(遍历并重命名文件):

#!/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风格数值循环):

for (( initialization; condition; step )); do
  # 循环体
done

代码示例(执行三次ping测试):

#!/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 循环的逻辑是:每次迭代前先检查条件,条件为真就继续执行循环体,直到条件变假为止。

格式:

while [ 条件判断 ]; do
  # 循环体
done

代码示例:逐行读取文件

#!/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 正好相反:只要条件为假,就继续执行循环体,直到条件变为真才停止。可以理解为“直到……才停止”。

格式:

until [ 条件判断 ]; do
  # 循环体
done

代码示例:等待服务端口启动

#!/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: 跳过当前循环的剩余部分,直接开始下一次迭代。

代码示例:在循环中处理文件

#!/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 更清晰、更直观,特别适合根据单个变量的值来做匹配。

格式:

case $variable in
  pattern1)
    # 匹配 pattern1 时执行
    ;;
  pattern2|pattern3)
    # 匹配 pattern2 或 pattern3 时执行
    ;;
  *)
    # 默认情况,当以上模式都不匹配时执行
    ;;
esac

代码示例:脚本参数解析

#!/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. 文件权限检查:
#!/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 是文件测试操作符,分别检查读、写、执行权限。&& 是逻辑与操作符,要求所有条件都为真才执行 then 块。
  1. 字符串与逻辑判断:
#!/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 来精确控制循环,直接打印出范围内的偶数。
  1. for 循环与通配符:
#!/bin/bash
for logfile in /var/log/*.log; do
  if [ -s "$logfile" ]; then
    echo "找到非空日志文件: $(basename "$logfile")"
  fi
done
  • 解析: *.log 是一个通配符,for 循环会遍历所有匹配的文件名。-s 文件测试操作符用于判断文件大小是否大于零。basename 命令用于提取文件名,去除路径。
  1. 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
  1. until 循环与命令退出码:
#!/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 循环的条件是命令本身(grep -q ...)。只要 grep 找不到字符串(退出码非0,条件为假),循环就继续。一旦找到(退出码为0,条件为真),循环终止。
  1. 嵌套循环与 break n
#!/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 语句与通配符:
#!/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 语句的模式支持通配符,如 *(匹配任意字符序列)。| 用于分隔多个模式,表示“或”。
  1. select 菜单:
#!/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 中。

总结

流程控制是Shell脚本从“简单顺序执行”走向“智能自动化”的分水岭。条件判断让脚本能根据实际情况做决策,循环结构让重复工作变得轻松,分支选择则让代码更清晰易读。配合上 breakcontinue 这些控制语句,几乎可以应对所有日常运维和开发中的自动化需求。掌握好这些基础,再复杂的脚本也能拆解出清晰的逻辑。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
C++动态数组初始化怎么写?常用语句与代码示例
C++动态数组初始化怎么写?常用语句与代码示例

深入解析C++中动态数组的初始化机制,涵盖new操作符的不同用法、基本类型与类对象的初始化差异,以及为何在现代C++开发中应优先使用std::vector。

using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。