linux shell编程实战 04 条件判断与流程控制

在Shell编程中,我们经常需要根据不同的情况执行不同的操作,或者重复执行某些命令。本章将学习如何通过条件判断和流程控制让脚本具备"判断能力"和"循环处理能力",这些都是编写实用脚本的核心知识。

4.1 条件判断基础:test命令与[]表达式

条件判断是指通过检查某个条件是否成立,来决定是否执行特定的命令。Shell中最基础的条件判断方式是test命令和[]表达式,它们功能完全相同,后者是前者的简化写法。

4.1.1 认识条件判断

简单来说,条件判断就像生活中的"如果...就...":

  • 如果文件存在,就处理它
  • 如果数值大于100,就发出警告
  • 如果字符串为空,就提示用户输入

在Shell中,条件判断的结果只有两种:成立 (返回0)或不成立 (返回非0)。我们可以通过$?变量查看上一个命令的返回值(0表示成功/成立,非0表示失败/不成立)。

4.1.2 使用test命令

test命令的基本语法:

bash 复制代码
test 条件

示例1:检查文件是否存在

bash 复制代码
# 检查当前目录是否有hello.sh文件
test -f "hello.sh"
echo $?  # 如果文件存在,输出0;否则输出1

示例2:检查字符串是否非空

bash 复制代码
# 检查变量是否有值
name="张三"
test -n "$name"
echo $?  # 输出0(字符串非空,条件成立)

empty_str=""
test -n "$empty_str"
echo $?  # 输出1(字符串为空,条件不成立)

示例3:比较两个整数

bash 复制代码
a=10
b=20
test $a -lt $b  # -lt表示"小于"
echo $?  # 输出0(10<20成立)

4.1.3 使用[]表达式(推荐)

[]test命令的简化写法,更直观易用,语法:

bash 复制代码
[ 条件 ]  # 注意:条件前后必须有空格

示例:用[]重写上面的test命令

bash 复制代码
# 检查文件是否存在
[ -f "hello.sh" ]
echo $?

# 检查字符串
name="张三"
[ -n "$name" ]
echo $?

# 比较整数
a=10
b=20
[ $a -lt $b ]
echo $?

为什么推荐[]?

  • 写法更简洁,视觉上更像"条件判断"
  • 是Shell脚本中最常用的条件判断形式
  • 避免了test命令可能带来的语法歧义

4.1.4 常用条件类型及运算符

条件判断主要分为三类:文件测试、字符串测试和数值测试。

1. 文件测试(检查文件/目录属性)
运算符 含义 示例
-f 文件 文件是否存在且为普通文件 [ -f "test.txt" ]
-d 目录 目录是否存在 [ -d "mydir" ]
-e 路径 路径(文件或目录)是否存在 [ -e "path" ]
-r 文件 文件是否存在且可读 [ -r "test.txt" ]
-w 文件 文件是否存在且可写 [ -w "test.txt" ]
-x 文件 文件是否存在且可执行 [ -x "script.sh" ]

示例

bash 复制代码
file="test.txt"

# 检查文件是否存在
if [ -e "$file" ]; then
    echo "$file 存在"
else
    echo "$file 不存在"
fi
2. 字符串测试(检查字符串属性或比较)
运算符 含义 示例
-n 字符串 字符串是否非空(长度>0) [ -n "$name" ]
-z 字符串 字符串是否为空(长度=0) [ -z "$empty" ]
字符串1 = 字符串2 两个字符串是否相等 [ "$a" = "$b" ]
字符串1 != 字符串2 两个字符串是否不相等 [ "$a" != "$b" ]

示例

bash 复制代码
username="admin"
input="admin"

# 比较两个字符串是否相等
if [ "$input" = "$username" ]; then
    echo "用户名正确"
else
    echo "用户名错误"
fi

注意:字符串比较时,变量必须用双引号括起来,否则当变量为空时会导致语法错误。

3. 数值测试(整数比较)
运算符 含义 示例
num1 -eq num2 等于(equal) [ 10 -eq 10 ]
num1 -ne num2 不等于(not equal) [ 10 -ne 20 ]
num1 -gt num2 大于(greater than) [ 20 -gt 10 ]
num1 -lt num2 小于(less than) [ 10 -lt 20 ]
num1 -ge num2 大于等于 [ 10 -ge 10 ]
num1 -le num2 小于等于 [ 10 -le 20 ]

示例

bash 复制代码
age=17

# 检查是否成年
if [ $age -ge 18 ]; then
    echo "已成年"
else
    echo "未成年"
fi

4.1.5 逻辑运算符(组合多个条件)

有时需要同时判断多个条件,这就需要用到逻辑运算符:

运算符 含义 用法
-a 逻辑与(两个条件都成立) [ 条件1 -a 条件2 ]
-o 逻辑或(至少一个条件成立) [ 条件1 -o 条件2 ]
! 逻辑非(取反) [ ! 条件 ]

示例1:逻辑与(-a)

bash 复制代码
# 检查文件是否存在且可写
file="data.txt"
if [ -f "$file" -a -w "$file" ]; then
    echo "$file 存在且可写"
else
    echo "$file 不存在或不可写"
fi

示例2:逻辑非(!)

bash 复制代码
# 检查文件是否不存在
file="nonexistent.txt"
if [ ! -e "$file" ]; then
    echo "$file 不存在,创建它..."
    touch "$file"  # 创建文件
fi

4.2 if语句:实现分支判断

if语句是Shell中最常用的流程控制结构,它能根据条件的真假执行不同的命令块。if语句有三种形式:单分支、双分支和多分支。

4.2.1 单分支if语句(如果...就...)

语法

bash 复制代码
if [ 条件 ]; then
    # 条件成立时执行的命令
fi

说明

  • then表示条件成立后要执行的命令块开始
  • fiif的反写,表示if语句结束
  • 条件成立时执行thenfi之间的命令,否则不执行

示例:检查文件是否存在,存在则输出内容

bash 复制代码
#!/bin/bash
file="test.txt"

# 如果文件存在,就显示其内容
if [ -f "$file" ]; then
    echo "$file 的内容如下:"
    cat "$file"  # 显示文件内容
fi

echo "脚本执行结束"  # 无论条件是否成立,都会执行

4.2.2 双分支if-else语句(如果...就...否则...)

语法

bash 复制代码
if [ 条件 ]; then
    # 条件成立时执行
else
    # 条件不成立时执行
fi

说明

  • 条件成立执行then后的命令块
  • 条件不成立执行else后的命令块
  • 两个分支必选其一

示例:检查文件是否存在,不存在则创建

bash 复制代码
#!/bin/bash
file="notes.txt"

if [ -f "$file" ]; then
    echo "$file 已存在"
else
    echo "$file 不存在,正在创建..."
    touch "$file"  # 创建文件
    echo "创建成功"
fi

4.2.3 多分支if-elif-else语句(多条件判断)

当需要判断多个条件时,使用if-elif-else结构:

语法

bash 复制代码
if [ 条件1 ]; then
    # 条件1成立时执行
elif [ 条件2 ]; then
    # 条件2成立时执行
elif [ 条件3 ]; then
    # 条件3成立时执行
else
    # 所有条件都不成立时执行(可选)
fi

说明

  • elif是"else if"的缩写,表示"否则如果"
  • 依次检查条件,找到第一个成立的条件后执行对应的命令块,然后跳过后续所有条件
  • else是可选的,当所有条件都不成立时执行

示例:根据分数判断等级

bash 复制代码
#!/bin/bash
read -p "请输入你的分数(0-100):" score

# 先检查输入是否为有效的数字
if [ $score -lt 0 ] || [ $score -gt 100 ]; then
    echo "错误:分数必须在0-100之间"
else
    # 根据分数判断等级
    if [ $score -ge 90 ]; then
        echo "等级:优秀"
    elif [ $score -ge 80 ]; then
        echo "等级:良好"
    elif [ $score -ge 60 ]; then
        echo "等级:及格"
    else
        echo "等级:不及格"
    fi
fi

4.2.4 if语句的嵌套

if语句内部可以再包含if语句,实现更复杂的判断逻辑:

示例:嵌套if判断

bash 复制代码
#!/bin/bash
read -p "请输入年龄:" age

if [ $age -ge 18 ]; then
    echo "已成年"
    read -p "是否有身份证(y/n):" has_id
    # 嵌套if判断
    if [ "$has_id" = "y" ]; then
        echo "可以办理银行卡"
    else
        echo "请先办理身份证"
    fi
else
    echo "未成年,无法办理银行卡"
fi

注意:嵌套层数不宜过多(建议不超过2层),否则会让脚本难以阅读和维护。

4.2.5 常见错误与注意事项

  1. 忘记写then
bash 复制代码
# 错误
if [ $a -gt 10 ]
    echo "a大于10"
fi

# 正确
if [ $a -gt 10 ]; then
    echo "a大于10"
fi
  1. 条件前后缺少空格
bash 复制代码
# 错误([和]前后必须有空格)
if [$a -gt 10]; then
    ...
fi

# 正确
if [ $a -gt 10 ]; then
    ...
fi
  1. 字符串比较不用双引号
bash 复制代码
# 危险(如果var为空,会导致语法错误)
if [ $var = "test" ]; then
    ...
fi

# 安全
if [ "$var" = "test" ]; then
    ...
fi

4.3 case语句:多值匹配判断

当需要判断一个变量与多个固定值匹配时,case语句比if-elif-else更简洁直观。比如处理菜单选择、命令行选项等场景。

4.3.1 case语句基本语法

bash 复制代码
case 变量 in
    值1)
        # 变量等于值1时执行
        ;;
    值2)
        # 变量等于值2时执行
        ;;
    值3)
        # 变量等于值3时执行
        ;;
    *)
        # 变量不匹配任何值时执行(可选)
        ;;
esac

说明

  • case后面是要判断的变量
  • in是关键字,用于引入匹配的值列表
  • 每个"值)"后面是对应的命令块,以;;结束(表示跳出case)
  • *表示"其他所有情况",类似else
  • esaccase的反写,表示case语句结束

4.3.2 case语句示例

示例1:简单的菜单选择

bash 复制代码
#!/bin/bash
echo "请选择操作:"
echo "1. 查看日期"
echo "2. 查看当前目录"
echo "3. 查看系统时间"
echo "4. 退出"

read -p "请输入选项(1-4):" choice

case $choice in
    1)
        echo "当前日期:$(date +%Y-%m-%d)"
        ;;
    2)
        echo "当前目录内容:"
        ls  # 显示当前目录文件
        ;;
    3)
        echo "当前时间:$(date +%H:%M:%S)"
        ;;
    4)
        echo "再见!"
        exit 0  # 退出脚本
        ;;
    *)
        echo "无效选项,请输入1-4"
        ;;
esac

示例2:判断输入的字符类型

bash 复制代码
#!/bin/bash
read -p "请输入一个字符:" char

case $char in
    [0-9])  # 匹配数字
        echo "你输入的是数字"
        ;;
    [a-z])  # 匹配小写字母
        echo "你输入的是小写字母"
        ;;
    [A-Z])  # 匹配大写字母
        echo "你输入的是大写字母"
        ;;
    *)  # 其他字符
        echo "你输入的是特殊字符"
        ;;
esac

4.3.3 case与if的选择

  • 当判断条件是多个固定值 时,用case更简洁(如菜单选项)
  • 当需要进行范围判断 (如>、<、>=)时,必须用if
  • 条件较少(2-3个)时,ifcase均可
  • 条件较多(4个以上)时,case更易读

4.4 流程控制辅助命令

Shell提供了几个辅助命令,用于控制脚本的执行流程,配合条件判断和循环使用。

4.4.1 exit命令:终止脚本执行

exit命令用于立即结束整个脚本的执行,并返回一个状态码:

语法

bash 复制代码
exit [状态码]
  • 状态码是0-255的整数,0表示"成功",非0表示"失败"
  • 不指定状态码时,默认使用上一个命令的退出状态码

示例:脚本出错时退出

bash 复制代码
#!/bin/bash
file="data.txt"

# 检查文件是否存在,不存在则退出
if [ ! -f "$file" ]; then
    echo "错误:$file 不存在"
    exit 1  # 非0状态码表示失败
fi

# 文件存在则继续处理
echo "开始处理 $file..."
# 处理文件的命令...
exit 0  # 0表示成功结束

4.4.2 break和continue命令(提前了解)

这两个命令通常用于循环结构(下一章学习),但在这里先简单介绍:

  • break:跳出当前循环,不再执行后续循环
  • continue:跳过本次循环的剩余部分,直接进入下一次循环

示例(提前预览循环)

bash 复制代码
#!/bin/bash
# 打印1-10中的奇数(用continue跳过偶数)
echo "1-10中的奇数:"
for ((i=1; i<=10; i++)); do
    if [ $((i % 2)) -eq 0 ]; then
        continue  # 是偶数则跳过
    fi
    echo $i
done

4.5 实战示例:文件管理小工具

综合本章所学,编写一个简单的文件管理工具,实现创建、删除、查看文件等功能:

bash 复制代码
#!/bin/bash
# 文件管理小工具
# 功能:创建文件、删除文件、查看文件内容

echo "===== 文件管理工具 ====="
echo "1. 创建新文件"
echo "2. 删除文件"
echo "3. 查看文件内容"
echo "4. 退出"
echo "======================"

read -p "请选择功能(1-4):" choice

case $choice in
    1)
        read -p "请输入要创建的文件名:" filename
        # 检查文件是否已存在
        if [ -e "$filename" ]; then
            read -p "$filename 已存在,是否覆盖?(y/n):" overwrite
            if [ "$overwrite" != "y" ]; then
                echo "取消创建"
                exit 0
            fi
        fi
        # 创建文件
        touch "$filename"
        echo "$filename 创建成功"
        ;;
    2)
        read -p "请输入要删除的文件名:" filename
        # 检查文件是否存在
        if [ ! -e "$filename" ]; then
            echo "错误:$filename 不存在"
            exit 1
        fi
        # 确认删除
        read -p "确定要删除 $filename 吗?(y/n):" confirm
        if [ "$confirm" = "y" ]; then
            rm "$filename"
            echo "$filename 已删除"
        else
            echo "取消删除"
        fi
        ;;
    3)
        read -p "请输入要查看的文件名:" filename
        # 检查文件是否存在且是普通文件
        if [ ! -f "$filename" ]; then
            echo "错误:$filename 不存在或不是普通文件"
            exit 1
        fi
        # 检查文件是否为空
        if [ -s "$filename" ]; then
            echo "$filename 的内容:"
            echo "----------------------"
            cat "$filename"
            echo "----------------------"
        else
            echo "$filename 是空文件"
        fi
        ;;
    4)
        echo "谢谢使用,再见!"
        exit 0
        ;;
    *)
        echo "错误:无效选项,请输入1-4"
        exit 1
        ;;
esac

脚本说明

  1. 使用case语句实现菜单选择
  2. 每个功能都有条件判断(文件是否存在、用户确认等)
  3. 用到了touch(创建文件)、rm(删除文件)、cat(查看文件)等基础Linux命令
  4. 有错误处理和用户交互,更贴近实际工具的使用场景

小结

本章学习了Shell中的条件判断与流程控制,主要内容包括:

  • 条件判断基础test命令和[]表达式的使用,以及文件测试、字符串测试、数值测试三类条件运算符
  • if语句:单分支(if)、双分支(if-else)、多分支(if-elif-else)的语法和应用场景,以及嵌套if的使用
  • case语句:多值匹配的语法和适用场景,与if语句的选择建议
  • 流程控制辅助命令exit(终止脚本)、breakcontinue(循环控制,下章详细学习)

这些知识是让脚本具备"判断能力"的核心,通过合理使用条件判断,脚本可以根据不同情况做出不同响应,大大提高灵活性。

下一章我们将学习循环结构,它能让脚本重复执行命令,非常适合批量处理任务(如批量重命名文件、批量处理数据等)。

相关推荐
至善迎风5 小时前
如何让 Ubuntu 服务器在你关机后继续执行命令
linux·服务器·ubuntu·进程
云心雨禅6 小时前
DNS工作原理:从域名到IP
运维·前端·网络协议·tcp/ip·github
岚天start7 小时前
CentOS系统yum list使用指南
linux·运维·centos·list·yum·repoquery
纳切威7 小时前
CentOS 9 系统安装
linux
HIT_Weston7 小时前
13、【Ubuntu】【VSCode】VSCode 断联问题分析:hostname(一)
linux·vscode·ubuntu
博图光电7 小时前
博图机械臂:以智能精度,重塑多行业自动化新生态
运维·自动化
小安运维日记7 小时前
RHCA - DO374 | Day01:使用红帽Ansible自动化平台开发剧本
运维·服务器·云原生·自动化·云计算·ansible
刘岩Tony8 小时前
ssh别名和多服务器同步文件
运维·服务器·ssh
zzy20887402718 小时前
自定义服务器实现时间同步
运维·服务器