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 ]; then
    if [ 条件2 ]; then
        命令1
    fi
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/bash

if [ -e "data.txt" ]; then
    echo "文件存在"
    cat data.txt
else
    echo "文件不存在,创建中..."
    touch data.txt
fi
2. 检查参数数量
复制代码
#!/bin/bash

if [ $# -ne 2 ]; then
    echo "错误:需要两个参数" >&2
    echo "用法:$0 参数1 参数2" >&2
    exit 1
else
    echo "参数1: $1"
    echo "参数2: $2"
fi
3. 数值比较
复制代码
#!/bin/bash

read -p "请输入一个数字: " num

if [ "$num" -lt 0 ]; then
    echo "负数"
elif [ "$num" -eq 0 ]; then
    echo "零"
else
    echo "正数"
fi
4. 字符串匹配
复制代码
#!/bin/bash

read -p "请输入yes或no: " answer

if [ "$answer" = "yes" ]; then
    echo "你选择了yes"
elif [ "$answer" = "no" ]; then
    echo "你选择了no"
else
    echo "无效输入"
fi
5. 检查命令执行结果
复制代码
#!/bin/bash

if grep -q "error" log.txt; then
    echo "日志中发现错误"
    mail -s "系统异常" admin@example.com < log.txt
else
    echo "系统正常"
fi
6. 嵌套条件
复制代码
#!/bin/bash

read -p "请输入年龄: " age

if [ "$age" -ge 18 ]; then
    if [ "$age" -lt 60 ]; then
        echo "成年人"
    else
        echo "老年人"
    fi
else
    echo "未成年人"
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 通用错误(例如参数错误、文件不存在)。
2 Shell 内置命令错误(如 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" ]; then
    echo "错误:文件 data.txt 不存在!"
    exit 1  # 退出并返回错误码 1
fi

echo "文件存在,继续执行..."
exit 0  # 正常退出
  • 若文件不存在,脚本输出错误信息后终止,返回 1;否则返回 0
3. 根据命令执行结果决定是否退出
复制代码
#!/bin/bash
# 尝试创建目录
mkdir -p "/data/app"
if [ $? -ne 0 ]; then  # $? 存储上一条命令的状态码
    echo "创建目录失败!"
    exit 2  # 返回错误码 2
fi

echo "目录创建成功"
exit 0
  • 通过 $? 判断前一条命令(mkdir)是否执行成功,失败则退出并返回 2
4. 在函数中退出脚本
复制代码
#!/bin/bash
check_disk_space() {
    free_space=$(df -h / | awk 'NR==2 {print $4}')
    if [ "$free_space" -lt "10G" ]; then
        echo "磁盘空间不足!"
        exit 100  # 在函数中直接退出脚本,返回 100
    fi
}

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" ]; then
    echo "错误:必须以 root 身份运行!" >&2  # 错误信息输出到 stderr
    exit 1  # 非 root 用户,返回 1
fi

# 检查磁盘空间
free_space=$(df -BM / | awk 'NR==2 {print $4}' | tr -d 'M')
if [ "$free_space" -lt 100 ]; then
    echo "警告:磁盘剩余空间不足 100MB!"
    exit 2  # 空间不足,返回 2
fi

echo "脚本执行成功"
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
do
    echo "当前水果:$fruit"
done

输出

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

示例

复制代码
for ((i=1; i<=5; i++))
do
    echo "迭代次数:$i"
done

输出

复制代码
迭代次数:1
迭代次数:2
迭代次数:3
迭代次数:4
迭代次数:5

二、while 循环

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

输出

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

三、until 循环

1. 基本语法(条件为假时循环)
复制代码
until [ 条件 ]
do
    命令序列
done
  • 执行逻辑 :先判断条件,若为假则执行循环体,重复直至条件为真(与 while 相反)。
2. 示例
复制代码
n=10
until [ $n -le 0 ]
do
    echo "$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
    do
        if [ $i -eq 3 ]; then
            break  # 当 i=3 时,跳出循环
        fi
        echo $i
    done

输出

复制代码
1
2
2. continue:跳过当前迭代,继续下一次循环
  • 用法

    复制代码
    for i in 1 2 3 4 5
    do
        if [ $i -eq 3 ]; then
            continue  # 当 i=3 时,跳过本次循环
        fi
        echo $i
    done

输出

复制代码
1
2
4
5
3. break ncontinue n:控制多层循环
  • n 表示跳出 / 跳过的循环层数(默认 n=1,即当前层)。

  • 示例(双层循环)

    复制代码
    for i in a b c
    do
        for j in 1 2 3
        do
            if [ $j -eq 2 ]; then
                break 2  # 跳出两层循环(即整个循环)
            fi
            echo "$i-$j"
        done
    done

输出

复制代码
a-1

五、循环常见应用场景

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

输出

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

六、注意事项

  1. 变量引用加引号

    避免列表元素包含空格时被错误分割:

    复制代码
    # 错误(元素包含空格时分割错误)
    for str in "hello world" "hi bash"
    do
        echo $str  # 正确输出每个元素
    done
  2. 避免无限循环

    while/until 循环中确保条件最终为真 / 假,否则需用 Ctrl+C 强制终止。

  3. 多层循环性能

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

七、循环与数组结合

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

输出

复制代码
索引 0:a
索引 1:b
索引 2:c

总结

循环类型 特点 适用场景
for 循环 遍历固定列表或数值范围,简洁直观 批量处理文件、已知次数的任务
while 循环 条件为真时持续执行,适合未知次数的循环 读取文件、交互式操作
until 循环 条件为假时持续执行,逻辑与 while 相反 逆序计数、条件反转场景
break/continue 控制循环流程,跳出或跳过迭代 复杂逻辑分支、提前终止循环
相关推荐
博语小屋7 分钟前
进程初识之进程状态
linux
吉凶以情迁1 小时前
window服务相关问题探索 go语言服务开发探索调试
linux·服务器·开发语言·网络·golang
卍郝凝卍1 小时前
云上服务器常见的存储方式和类型
大数据·服务器·数据库
柏木乃一2 小时前
Linux初步认识与指令与权限
linux·运维·服务器·shell·权限
189228048612 小时前
NX947NX955美光固态闪存NX962NX966
大数据·服务器·网络·人工智能·科技
Joemt2 小时前
ubuntu源码编译安装cmake高版本、pybind11安装、crow使用
linux·运维·ubuntu
真智AI3 小时前
打破数据质量瓶颈:用n8n实现30秒专业数据质量报告自动化
大数据·运维·人工智能·python·自动化
nightunderblackcat3 小时前
进阶向:自动化天气查询工具(API调用)
运维·自动化
huohuopro3 小时前
在linux(ubuntu)服务器上安装NTQQ并使用
linux·ubuntu