shell脚本之条件判断,循环控制,exit详解

if条件语句的语法及案例

一、基本语法结构

1. 单条件判断
if [ 条件 ]; then命令1命令2...
fi
2. 双分支(if-else)
if [ 条件 ]; then条件为真时执行的命令
else条件为假时执行的命令
fi
3. 多分支(if-elif-else)
if [ 条件1 ]; then命令1
elif [ 条件2 ]; then命令2
else命令3
fi
4. 嵌套结构
if [ 条件1 ]; thenif [ 条件2 ]; then命令1fi
else命令2
fi
5.多条件判断语句case
case 变量名 in
值1)
指令1
;;
值2)
指令2
;;
值3)
指令3
;;
*)
默认
esac

二、条件测试语法

1. 数值比较
if [ $a -eq 10 ]; then ...    # 等于
if [ $a -ne 10 ]; then ...    # 不等于
if [ $a -gt 10 ]; then ...    # 大于
if [ $a -lt 10 ]; then ...    # 小于
if [ $a -ge 10 ]; then ...    # 大于等于
if [ $a -le 10 ]; then ...    # 小于等于
2. 字符串比较
if [ "$str" = "hello" ]; then ...    # 等于(注意用双引号)
if [ "$str" != "hello" ]; then ...   # 不等于
if [ -z "$str" ]; then ...           # 字符串为空
if [ -n "$str" ]; then ...           # 字符串非空
3. 文件测试
if [ -e "file.txt" ]; then ...      # 文件或目录存在
if [ -f "file.txt" ]; then ...      # 文件存在且为普通文件
if [ -d "dir" ]; then ...           # 目录存在
if [ -r "file.txt" ]; then ...      # 文件可读
if [ -w "file.txt" ]; then ...      # 文件可写
if [ -x "script.sh" ]; then ...     # 文件可执行
4. 逻辑组合
# 使用 &&(逻辑与)
if [ -f "file.txt" ] && [ -r "file.txt" ]; then ...# 使用 ||(逻辑或)
if [ $a -eq 1 ] || [ $a -eq 2 ]; then ...# 使用 !(逻辑非)
if [ ! -d "dir" ]; then ...

三、案例示例

1. 判断文件是否存在
#!/bin/bashif [ -e "data.txt" ]; thenecho "文件存在"cat data.txt
elseecho "文件不存在,创建中..."touch data.txt
fi
2. 检查参数数量
#!/bin/bashif [ $# -ne 2 ]; thenecho "错误:需要两个参数" >&2echo "用法:$0 参数1 参数2" >&2exit 1
elseecho "参数1: $1"echo "参数2: $2"
fi
3. 数值比较
#!/bin/bashread -p "请输入一个数字: " numif [ "$num" -lt 0 ]; thenecho "负数"
elif [ "$num" -eq 0 ]; thenecho "零"
elseecho "正数"
fi
4. 字符串匹配
#!/bin/bashread -p "请输入yes或no: " answerif [ "$answer" = "yes" ]; thenecho "你选择了yes"
elif [ "$answer" = "no" ]; thenecho "你选择了no"
elseecho "无效输入"
fi
5. 检查命令执行结果
#!/bin/bashif grep -q "error" log.txt; thenecho "日志中发现错误"mail -s "系统异常" admin@example.com < log.txt
elseecho "系统正常"
fi
6. 嵌套条件
#!/bin/bashread -p "请输入年龄: " ageif [ "$age" -ge 18 ]; thenif [ "$age" -lt 60 ]; thenecho "成年人"elseecho "老年人"fi
elseecho "未成年人"
fi
7.case语句使用 
由用户从键盘输入一个字符,并判断该字符是否为字母、数字或者其他字符, 并输出
相应的提示信息。[root@kittod ~]# cat in.sh
#!/bin/bash
read -p "Please enter a character, press enter to continue: " KEY
case "$KEY" in
[a-z]|[A-Z])
echo "Input is letter"
;;
[0-9])
echo "Input is number"
;;
*)
echo "Input is other characters"
esac

四、高级语法

1. 使用 [[ ]] 替代 [ ]
# [[ ]] 支持更复杂的表达式
if [[ "$str" == hello* ]]; then ...    # 模式匹配
if [[ $a -gt 10 && $b -lt 20 ]]; then ...    # 逻辑组合
2. 使用 (( )) 进行数值比较
if (( a > 10 )); then ...    # 无需引号,支持算术表达式
3. 使用 test 命令
if test -f "file.txt"; then ...    # 等同于 [ -f "file.txt" ]

五、常见错误及注意事项

  1. 空格问题
    [ 和 ] 内部必须有空格,例如:

    # 正确
    if [ "$a" -eq 10 ]; then ...# 错误(缺少空格)
    if ["$a"-eq 10]; then ...
    
  2. 变量引用加引号
    防止变量为空时导致语法错误:

    # 正确
    if [ -z "$str" ]; then ...# 错误(当 $str 为空时会变成 [ -z ],语法错误)
    if [ -z $str ]; then ...
    
  3. 整数比较用 -eq,而非 =

    # 正确(数值比较)
    if [ "$a" -eq 10 ]; then ...# 错误(字符串比较)
    if [ "$a" = 10 ]; then ...
    

六、总结

语法结构适用场景示例
[ 条件 ]传统条件测试[ -f "file.txt" ]
[[ 条件 ]]增强型条件测试(推荐)[[ $a -gt 10 && $b -lt 20 ]]
(( 条件 ))纯数值比较(( a > 10 ))
test 条件等同于 [ ]test -d "dir"

合理使用 if 条件语句可以让脚本根据不同情况执行不同逻辑,增强脚本的灵活性和健壮性。

exit详细介绍

一、基本语法

exit [N]
  • N:可选参数,表示退出状态码(整数,范围通常为 0~255)。
    • 若省略 Nexit 将返回最后一条命令的执行状态码
    • 状态码可通过 $? 变量在脚本外获取(例如在终端中执行 echo $?)。

二、退出状态码的含义

1. 标准状态码
  • 0:表示脚本正常执行完毕(无错误)。
  • 非 0:表示脚本异常终止或执行过程中出现错误(常见值:1~255)。
2. 常用非零状态码(约定俗成)
状态码含义说明
1通用错误(例如参数错误、文件不存在)。
2Shell 内置命令错误(如 cd 命令失败)。
126命令存在但不可执行(如脚本无执行权限)。
127命令不存在(如拼写错误的命令)。
130脚本被中断信号(如 Ctrl+C)终止。
255非法退出状态码(超出范围的数值会被取模为 255,例如 exit 300 等价于 exit 44)。

三、exit 的典型用法场景

1. 正常退出脚本(返回 0
#!/bin/bash
echo "脚本执行完成"
exit  # 等价于 exit 0
 
  • 执行后,通过 echo $? 可获取状态码 0
2. 异常退出并返回错误码
#!/bin/bash
# 检查文件是否存在
if [ ! -f "data.txt" ]; thenecho "错误:文件 data.txt 不存在!"exit 1  # 退出并返回错误码 1
fiecho "文件存在,继续执行..."
exit 0  # 正常退出
 
  • 若文件不存在,脚本输出错误信息后终止,返回 1;否则返回 0
3. 根据命令执行结果决定是否退出
#!/bin/bash
# 尝试创建目录
mkdir -p "/data/app"
if [ $? -ne 0 ]; then  # $? 存储上一条命令的状态码echo "创建目录失败!"exit 2  # 返回错误码 2
fiecho "目录创建成功"
exit 0
 
  • 通过 $? 判断前一条命令(mkdir)是否执行成功,失败则退出并返回 2
4. 在函数中退出脚本
#!/bin/bash
check_disk_space() {free_space=$(df -h / | awk 'NR==2 {print $4}')if [ "$free_space" -lt "10G" ]; thenecho "磁盘空间不足!"exit 100  # 在函数中直接退出脚本,返回 100fi
}check_disk_space
echo "磁盘空间充足"  # 若空间不足,此行不会执行
exit 0
 
  • 函数中调用 exit 会直接终止脚本,无需返回值传递。

四、注意事项

1. 状态码的范围限制
  • 状态码必须是 0~255 之间的整数,超出范围会被自动取模(例如 exit 300 等价于 exit 44,因为 300 mod 256 = 44)。
2. 子进程中的 exit
  • 在子 Shell(如 (...) 或管道)中执行 exit仅会终止子 Shell,不影响父脚本。例如:
    (echo "子 Shell 开始"exit 1  # 子 Shell 退出,返回码 1
    )
    echo "父脚本继续执行"  # 此行会执行
    echo $?  # 输出 1(子 Shell 的退出码)
    
3. 与 return 的区别
  • exit:用于终止整个脚本,并返回状态码给系统(适用于脚本主流程)。
  • return:用于终止当前函数,并返回状态码给调用者(仅在函数内部使用)。
    func() {return 5  # 函数返回 5,不终止脚本
    }
    func
    echo $?  # 输出 5(函数的返回值)
    

五、最佳实践

  1. 明确返回状态码
    • 正常执行时返回 0,错误时返回有意义的非零码(如 12 等),便于外部脚本或监控工具判断执行结果。
  2. 提前退出
    • 在脚本开头检查必要条件(如权限、文件存在性),不满足时尽早退出并提示错误。
  3. 避免滥用
    • 仅在需要终止脚本时使用 exit,避免在循环或分支中无意义地退出。

六、案例:带状态码的脚本

#!/bin/bash
# 检查当前用户是否为 root
if [ "$USER" != "root" ]; thenecho "错误:必须以 root 身份运行!" >&2  # 错误信息输出到 stderrexit 1  # 非 root 用户,返回 1
fi# 检查磁盘空间
free_space=$(df -BM / | awk 'NR==2 {print $4}' | tr -d 'M')
if [ "$free_space" -lt 100 ]; thenecho "警告:磁盘剩余空间不足 100MB!"exit 2  # 空间不足,返回 2
fiecho "脚本执行成功"
exit 0  # 正常退出,返回 0
 
  • 执行结果:
    $ ./script.sh          # 非 root 用户执行
    错误:必须以 root 身份运行!
    $ echo $?              # 查看状态码
    1$ sudo ./script.sh     # root 用户执行且空间充足
    脚本执行成功
    $ echo $?
    0
    
 

通过合理使用 exit,可以让脚本更健壮、更易调试,尤其在自动化运维和脚本链调用中至关重要。

循环控制

一、for 循环

1. 基本语法(遍历列表)
for 变量名 in 列表元素
do命令序列
done

  • 列表元素:可以是空格分隔的字符串、文件列表、命令输出等。
  • 执行逻辑:变量依次取列表中的每个值,执行循环体。
2. 示例
示例 1:遍历字符串列表
for fruit in apple banana cherry
doecho "当前水果:$fruit"
done
 

输出

 
当前水果:apple
当前水果:banana
当前水果:cherry
示例 2:遍历文件列表
for file in /etc/*.conf
doif [ -f "$file" ]; thenecho "文件:$file"fi
done
示例 3:遍历命令输出
for line in $(cat users.txt)
doecho "用户:$line"
done
3. C 语言风格 for 循环(适用于数值迭代)
for ((初始值; 条件; 增量))
do命令序列
done
 
  • 初始值:变量初始化(如 i=1)。
  • 条件:循环继续的条件(如 i<=10)。
  • 增量:变量更新(如 i++)。
 

示例

 
for ((i=1; i<=5; i++))
doecho "迭代次数:$i"
done
 

输出

 
迭代次数:1
迭代次数:2
迭代次数:3
迭代次数:4
迭代次数:5

二、while 循环

1. 基本语法(条件为真时循环)
while [ 条件 ]
do命令序列
done
 
  • 执行逻辑:先判断条件,若为真则执行循环体,重复直至条件为假。
2. 示例
示例 1:数值累加
sum=0
i=1
while [ $i -le 10 ]
dosum=$((sum + i))i=$((i + 1))
done
echo "1+2+...+10 = $sum"
 

输出

 
1+2+...+10 = 55
示例 2:读取文件逐行处理
while read -r line
doecho "行内容:$line"
done < file.txt
3. 无限循环
while true
doecho "无限循环中...(按 Ctrl+C 终止)"sleep 1
done

三、until 循环

1. 基本语法(条件为假时循环)
until [ 条件 ]
do命令序列
done
 
  • 执行逻辑:先判断条件,若为假则执行循环体,重复直至条件为真(与 while 相反)。
2. 示例
n=10
until [ $n -le 0 ]
doecho "$n"n=$((n - 1))
done
 

输出

 
10
9
8
7
6
5
4
3
2
1

四、循环控制关键字

1. break:跳出当前循环
  • 用法
    for i in 1 2 3 4 5
    doif [ $i -eq 3 ]; thenbreak  # 当 i=3 时,跳出循环fiecho $i
    done
    
 

输出

 
1
2
2. continue:跳过当前迭代,继续下一次循环
  • 用法
    for i in 1 2 3 4 5
    doif [ $i -eq 3 ]; thencontinue  # 当 i=3 时,跳过本次循环fiecho $i
    done
    
 

输出

 
1
2
4
5
3. break n 和 continue n:控制多层循环
  • n 表示跳出 / 跳过的循环层数(默认 n=1,即当前层)。
  • 示例(双层循环)
    for i in a b c
    dofor j in 1 2 3doif [ $j -eq 2 ]; thenbreak 2  # 跳出两层循环(即整个循环)fiecho "$i-$j"done
    done
    
 

输出

 
a-1

五、循环常见应用场景

1. 批量文件操作
# 删除所有 .bak 文件
for file in *.bak
dorm -f "$file"echo "已删除:$file"
done
2. 进度条模拟
echo -n "进度:["
for ((i=0; i<=10; i++))
dosleep 0.5echo -n "#"
done
echo "] 完成"
 

输出

 
进度:##########] 完成
3. 交互式循环(用户输入控制)
while true
doread -p "是否继续?(y/n): " choicecase $choice in[Yy])echo "继续执行...";;[Nn])echo "退出程序"break;;*)echo "无效输入,请重新输入";;esac
done

六、注意事项

  1. 变量引用加引号
    避免列表元素包含空格时被错误分割:

    # 错误(元素包含空格时分割错误)
    for str in "hello world" "hi bash"
    doecho $str  # 正确输出每个元素
    done
    
  2. 避免无限循环
    在 while/until 循环中确保条件最终为真 / 假,否则需用 Ctrl+C 强制终止。

  3. 多层循环性能
    嵌套循环可能影响性能,尽量优化循环逻辑或使用更高效的工具(如 awksed)。

七、循环与数组结合

1. 遍历数组元素
arr=("apple" "banana" "cherry")
for fruit in "${arr[@]}"
doecho "数组元素:$fruit"
done
2. 遍历数组索引
arr=("a" "b" "c")
for i in "${!arr[@]}"  # ${!arr[@]} 获取数组索引
doecho "索引 $i:${arr[$i]}"
done
 

输出

 
索引 0:a
索引 1:b
索引 2:c

总结

循环类型特点适用场景
for 循环遍历固定列表或数值范围,简洁直观批量处理文件、已知次数的任务
while 循环条件为真时持续执行,适合未知次数的循环读取文件、交互式操作
until 循环条件为假时持续执行,逻辑与 while 相反逆序计数、条件反转场景
break/continue控制循环流程,跳出或跳过迭代复杂逻辑分支、提前终止循环

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

现代 Web 自动化测试框架对比:Playwright 与 Selenium 的深度剖析

现代 Web 自动化测试框架对比&#xff1a;Playwright 与 Selenium 的深度剖析 摘要&#xff1a;本文对 Playwright 与 Selenium 在开发适配性、使用难度、场景适用性及性能表现等方面进行了全面深入的对比分析。通过详细的技术实现细节阐述与实测数据支撑&#xff0c;为开发者…

系统架构设计(十):结构化编程

定义 结构化编程是一种遵循清晰逻辑结构、避免使用 goto 的编程方法。它强调使用有限的三种基本控制结构来组织程序&#xff0c;提高程序的可读性、可维护性和可测试性。 它是现代程序设计的基础&#xff0c;被广泛应用于命令式语言&#xff08;如 C、Pascal、Java&#xff0…

TC3xx学习笔记-UCB BMHD使用详解(二)

文章目录 前言Confirmation的定义Dual UCB: Confirmation StatesDual UCB: Errored State or ECC Error in the UCB Confirmation CodesECC Error in the UCB ContentDual Password UCB ORIG and COPY Re-programming UCB_BMHDx_ORIG and UCB_BMHDx_COPY (x 0-3)BMHD Protecti…

OTA与boot loader

OTA指的是无线升级&#xff0c;通常用于更新设备的固件或软件&#xff0c;用户不用手动操作&#xff0c;非常方便。而bootloader是启动时加载操作系统的程序&#xff0c;负责硬件初始化和启动流程。 首先&#xff0c;OTA是如何通过bootloader工作的。OTA下载更新包后&#xff0…

实验六:FPGA序列检测器实验

FPGA序列检测器实验(远程实验系统) 文章目录 FPGA序列检测器实验(远程实验系统)一、数字电路基础知识1. 时钟与同步2. 按键消抖原理代码讲解:分频与消抖3. 有限状态机(FSM)设计代码讲解:状态机编码与转移4. 边沿检测与信号同步5. 模块化设计二、实验数字电路整体思想三…

jenkins部署

开发者将代码push到git运维人员通过jenkins部署&#xff0c;自动到git上pull代码通过maven构建成jar包&#xff0c;并结合dockerfile打包成镜像&#xff0c;push docker镜像到docker registry通过k8s发起 发布/更新 服务 操作 通过Jenkins部署&#xff0c;自动到Git上PULL代码 …

BBR 的 buffer 动力学观感

这周很忙&#xff0c;今天还加了一天班&#xff0c;但还是抽空实现了五一在安徽泾县山区喝着一壶酒写的 BBR ProbeRTT 的想法&#xff0c;没多少行代码&#xff0c;它真就消除了带宽锯齿&#xff0c;皮了个鞋&#x1f45e;&#xff0c;昨天我还在群里说了今天再说说 BBR 的&…

第9讲、深入理解Scaled Dot-Product Attention

Scaled Dot-Product Attention是Transformer架构的核心组件&#xff0c;也是现代深度学习中最重要的注意力机制之一。本文将从原理、实现和应用三个方面深入剖析这一机制。 1. 基本原理 Scaled Dot-Product Attention的本质是一种加权求和机制&#xff0c;通过计算查询(Query…

el-tree结合checkbox实现数据回显

组件代码 <el-tree:data"vertiList"show-checkboxnode-key"id":props"defaultProps"ref"treeRefx"class"custom-tree"check-change"handleCheckChange"> </el-tree>获取选择的节点 handleCheckChan…

OpenResty 深度解析:构建高性能 Web 服务的终极方案

引言 openresty是什么&#xff1f;在我个人对它的理解来看相当于嵌入了lua的nginx; 我们在nginx中嵌入lua是为了不需要再重新编译,我们只需要重新修改lua脚本,随后重启即可; 一.lua指令序列 我们分别从初始化阶段&#xff0c;重写/访问阶段&#xff0c;内容阶段&#xff0c;日志…

多商户商城系统源码解析:开发直播电商APP的技术底层实战详解

随着直播电商的火爆&#xff0c;越来越多的创业者和企业都在寻求打造自己的多商户商城系统&#xff0c;以实现“人、货、场”三者的深度融合。然而&#xff0c;从一个简单的电商平台到一个功能完善的直播电商APP&#xff0c;其技术底层架构和实现过程并非一蹴而就。本文将从架构…

桌面端进程通信

以下是关于 Electron 桌面端进程通信的基本知识点总结: 一、Electron 进程模型基础 1. 进程类型与职责 进程类型职责权限主进程(Main)创建窗口、系统级操作、IPC中枢完全Node.js访问权限渲染进程(Renderer)展示Web内容、UI交互默认受限(可配置开启Node.js)预加载脚本(Prelo…

openEuler24.03 LTS下安装MySQL8.0.42

目录 前提步骤 删除原有mysql及maridb数据库 安装MySQL 启动MySQL 启动查看MySQL状态 设置MySQL开机自启动 查看登录密码 登录MySQL 修改密码及支持远程连接 远程连接MySQL 前提步骤 拥有openEuler24.03 LTS环境&#xff0c;可参考&#xff1a;Vmware下安装openEule…

idea 保证旧版本配置的同时,如何从低版本升到高版本

文章目录 前言idea 保证旧版本配置的同时,如何从低版本升到高版本1. 备份项目2. 下载最新的idea3. 安装安装包4. 导入idea2019旧配置5. 验证前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差,…

填坑记: 古董项目Apache POI 依赖异常排除

当你看到NoSuchMethodError的时候&#xff0c;不要慌&#xff0c;深呼吸&#xff0c;这可能只是JAR包版本的问题… 引子&#xff1a;一个平静的周二下午 那是一个看似平常的周二下午&#xff0c;系统运行良好&#xff0c;开发团队在有条不紊地推进着新功能的开发。突然&#x…

CAPL Class: TcpSocket (此类用于实现 TCP 网络通信 )

目录 Class: TcpSocketacceptopenclosebindconnectgetLastSocketErrorgetLastSocketErrorAsStringlistenreceivesendsetSocketOptionshutdown函数调用的基本流程服务器端的基本流程客户端的基本流程Class: TcpSocket学习笔记。来自CANoe帮助文档。 Class: TcpSocket accept /…

微信小程序的开发及问题解决

HttpClient 测试例子 SpringBootTest public class HttpClientTest {/*** 测试通过httpclient发送get方式的请求*/Testpublic void testGET() throws IOException {//创建httpclient对象CloseableHttpClient httpClient HttpClients.createDefault();//创建请求对象HttpGet ht…

foreach中使用await的问题

目录 1.说明 2.示例 3.解决方案 1.说明 在foreach中调用异步方法&#xff0c;即使使用了await&#xff0c;不会依次执行每个异步任务&#xff0c;也就是说Array.prototype.forEach不会等待 Promise 完成&#xff0c;即使你在回调函数中返回一个 Promise&#xff0c;forEach …

Linux调试生成核心存储文件

1.核心存储文件配置&#xff1a; 不知道理解对不对&#xff0c;Linux中的核心存储文件的配置是在/proc/sys/kernel/core_pattern中的&#xff0c;使用 cat /proc/sys/kernel/core_pattern # 打印出 |/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E表示核…

Compose笔记(二十三)--多点触控

这一节主要了解一下Compose中多点触控&#xff0c;在Jetpack Compose 中&#xff0c;多点触控处理需要结合Modifier和手势API来实现&#xff0c;一般通过组合 pointerInput、TransformableState 和 TransformModifier 来创建支持缩放、旋转和平移的组件。 一、 API 1. Pointer…