linux shell编程实战 04 条件判断与流程控制
在Shell编程中,我们经常需要根据不同的情况执行不同的操作,或者重复执行某些命令。本章将学习如何通过条件判断和流程控制让脚本具备"判断能力"和"循环处理能力",这些都是编写实用脚本的核心知识。
4.1 条件判断基础:test命令与[]表达式
条件判断是指通过检查某个条件是否成立,来决定是否执行特定的命令。Shell中最基础的条件判断方式是test
命令和[]
表达式,它们功能完全相同,后者是前者的简化写法。
4.1.1 认识条件判断
简单来说,条件判断就像生活中的"如果…就…":
- 如果文件存在,就处理它
- 如果数值大于100,就发出警告
- 如果字符串为空,就提示用户输入
在Shell中,条件判断的结果只有两种:成立(返回0)或不成立(返回非0)。我们可以通过$?
变量查看上一个命令的返回值(0表示成功/成立,非0表示失败/不成立)。
4.1.2 使用test命令
test
命令的基本语法:
test 条件
示例1:检查文件是否存在
# 检查当前目录是否有hello.sh文件
test -f "hello.sh"
echo $? # 如果文件存在,输出0;否则输出1
示例2:检查字符串是否非空
# 检查变量是否有值
name="张三"
test -n "$name"
echo $? # 输出0(字符串非空,条件成立)empty_str=""
test -n "$empty_str"
echo $? # 输出1(字符串为空,条件不成立)
示例3:比较两个整数
a=10
b=20
test $a -lt $b # -lt表示"小于"
echo $? # 输出0(10<20成立)
4.1.3 使用[]表达式(推荐)
[]
是test
命令的简化写法,更直观易用,语法:
[ 条件 ] # 注意:条件前后必须有空格
示例:用[]重写上面的test命令
# 检查文件是否存在
[ -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" ] |
示例:
file="test.txt"# 检查文件是否存在
if [ -e "$file" ]; thenecho "$file 存在"
elseecho "$file 不存在"
fi
2. 字符串测试(检查字符串属性或比较)
运算符 | 含义 | 示例 |
---|---|---|
-n 字符串 | 字符串是否非空(长度>0) | [ -n "$name" ] |
-z 字符串 | 字符串是否为空(长度=0) | [ -z "$empty" ] |
字符串1 = 字符串2 | 两个字符串是否相等 | [ "$a" = "$b" ] |
字符串1 != 字符串2 | 两个字符串是否不相等 | [ "$a" != "$b" ] |
示例:
username="admin"
input="admin"# 比较两个字符串是否相等
if [ "$input" = "$username" ]; thenecho "用户名正确"
elseecho "用户名错误"
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 ] |
示例:
age=17# 检查是否成年
if [ $age -ge 18 ]; thenecho "已成年"
elseecho "未成年"
fi
4.1.5 逻辑运算符(组合多个条件)
有时需要同时判断多个条件,这就需要用到逻辑运算符:
运算符 | 含义 | 用法 |
---|---|---|
-a | 逻辑与(两个条件都成立) | [ 条件1 -a 条件2 ] |
-o | 逻辑或(至少一个条件成立) | [ 条件1 -o 条件2 ] |
! | 逻辑非(取反) | [ ! 条件 ] |
示例1:逻辑与(-a)
# 检查文件是否存在且可写
file="data.txt"
if [ -f "$file" -a -w "$file" ]; thenecho "$file 存在且可写"
elseecho "$file 不存在或不可写"
fi
示例2:逻辑非(!)
# 检查文件是否不存在
file="nonexistent.txt"
if [ ! -e "$file" ]; thenecho "$file 不存在,创建它..."touch "$file" # 创建文件
fi
4.2 if语句:实现分支判断
if
语句是Shell中最常用的流程控制结构,它能根据条件的真假执行不同的命令块。if
语句有三种形式:单分支、双分支和多分支。
4.2.1 单分支if语句(如果…就…)
语法:
if [ 条件 ]; then# 条件成立时执行的命令
fi
说明:
then
表示条件成立后要执行的命令块开始fi
是if
的反写,表示if语句结束- 条件成立时执行
then
和fi
之间的命令,否则不执行
示例:检查文件是否存在,存在则输出内容
#!/bin/bash
file="test.txt"# 如果文件存在,就显示其内容
if [ -f "$file" ]; thenecho "$file 的内容如下:"cat "$file" # 显示文件内容
fiecho "脚本执行结束" # 无论条件是否成立,都会执行
4.2.2 双分支if-else语句(如果…就…否则…)
语法:
if [ 条件 ]; then# 条件成立时执行
else# 条件不成立时执行
fi
说明:
- 条件成立执行
then
后的命令块 - 条件不成立执行
else
后的命令块 - 两个分支必选其一
示例:检查文件是否存在,不存在则创建
#!/bin/bash
file="notes.txt"if [ -f "$file" ]; thenecho "$file 已存在"
elseecho "$file 不存在,正在创建..."touch "$file" # 创建文件echo "创建成功"
fi
4.2.3 多分支if-elif-else语句(多条件判断)
当需要判断多个条件时,使用if-elif-else
结构:
语法:
if [ 条件1 ]; then# 条件1成立时执行
elif [ 条件2 ]; then# 条件2成立时执行
elif [ 条件3 ]; then# 条件3成立时执行
else# 所有条件都不成立时执行(可选)
fi
说明:
elif
是"else if"的缩写,表示"否则如果"- 依次检查条件,找到第一个成立的条件后执行对应的命令块,然后跳过后续所有条件
else
是可选的,当所有条件都不成立时执行
示例:根据分数判断等级
#!/bin/bash
read -p "请输入你的分数(0-100):" score# 先检查输入是否为有效的数字
if [ $score -lt 0 ] || [ $score -gt 100 ]; thenecho "错误:分数必须在0-100之间"
else# 根据分数判断等级if [ $score -ge 90 ]; thenecho "等级:优秀"elif [ $score -ge 80 ]; thenecho "等级:良好"elif [ $score -ge 60 ]; thenecho "等级:及格"elseecho "等级:不及格"fi
fi
4.2.4 if语句的嵌套
if
语句内部可以再包含if
语句,实现更复杂的判断逻辑:
示例:嵌套if判断
#!/bin/bash
read -p "请输入年龄:" ageif [ $age -ge 18 ]; thenecho "已成年"read -p "是否有身份证(y/n):" has_id# 嵌套if判断if [ "$has_id" = "y" ]; thenecho "可以办理银行卡"elseecho "请先办理身份证"fi
elseecho "未成年,无法办理银行卡"
fi
注意:嵌套层数不宜过多(建议不超过2层),否则会让脚本难以阅读和维护。
4.2.5 常见错误与注意事项
- 忘记写then
# 错误
if [ $a -gt 10 ]echo "a大于10"
fi# 正确
if [ $a -gt 10 ]; thenecho "a大于10"
fi
- 条件前后缺少空格
# 错误([和]前后必须有空格)
if [$a -gt 10]; then...
fi# 正确
if [ $a -gt 10 ]; then...
fi
- 字符串比较不用双引号
# 危险(如果var为空,会导致语法错误)
if [ $var = "test" ]; then...
fi# 安全
if [ "$var" = "test" ]; then...
fi
4.3 case语句:多值匹配判断
当需要判断一个变量与多个固定值匹配时,case
语句比if-elif-else
更简洁直观。比如处理菜单选择、命令行选项等场景。
4.3.1 case语句基本语法
case 变量 in值1)# 变量等于值1时执行;;值2)# 变量等于值2时执行;;值3)# 变量等于值3时执行;;*)# 变量不匹配任何值时执行(可选);;
esac
说明:
case
后面是要判断的变量in
是关键字,用于引入匹配的值列表- 每个"值)"后面是对应的命令块,以
;;
结束(表示跳出case) *
表示"其他所有情况",类似else
esac
是case
的反写,表示case语句结束
4.3.2 case语句示例
示例1:简单的菜单选择
#!/bin/bash
echo "请选择操作:"
echo "1. 查看日期"
echo "2. 查看当前目录"
echo "3. 查看系统时间"
echo "4. 退出"read -p "请输入选项(1-4):" choicecase $choice in1)echo "当前日期:$(date +%Y-%m-%d)";;2)echo "当前目录内容:"ls # 显示当前目录文件;;3)echo "当前时间:$(date +%H:%M:%S)";;4)echo "再见!"exit 0 # 退出脚本;;*)echo "无效选项,请输入1-4";;
esac
示例2:判断输入的字符类型
#!/bin/bash
read -p "请输入一个字符:" charcase $char in[0-9]) # 匹配数字echo "你输入的是数字";;[a-z]) # 匹配小写字母echo "你输入的是小写字母";;[A-Z]) # 匹配大写字母echo "你输入的是大写字母";;*) # 其他字符echo "你输入的是特殊字符";;
esac
4.3.3 case与if的选择
- 当判断条件是多个固定值时,用
case
更简洁(如菜单选项) - 当需要进行范围判断(如
>、<、>=
)时,必须用if
- 条件较少(2-3个)时,
if
和case
均可 - 条件较多(4个以上)时,
case
更易读
4.4 流程控制辅助命令
Shell提供了几个辅助命令,用于控制脚本的执行流程,配合条件判断和循环使用。
4.4.1 exit命令:终止脚本执行
exit
命令用于立即结束整个脚本的执行,并返回一个状态码:
语法:
exit [状态码]
- 状态码是0-255的整数,0表示"成功",非0表示"失败"
- 不指定状态码时,默认使用上一个命令的退出状态码
示例:脚本出错时退出
#!/bin/bash
file="data.txt"# 检查文件是否存在,不存在则退出
if [ ! -f "$file" ]; thenecho "错误:$file 不存在"exit 1 # 非0状态码表示失败
fi# 文件存在则继续处理
echo "开始处理 $file..."
# 处理文件的命令...
exit 0 # 0表示成功结束
4.4.2 break和continue命令(提前了解)
这两个命令通常用于循环结构(下一章学习),但在这里先简单介绍:
break
:跳出当前循环,不再执行后续循环continue
:跳过本次循环的剩余部分,直接进入下一次循环
示例(提前预览循环):
#!/bin/bash
# 打印1-10中的奇数(用continue跳过偶数)
echo "1-10中的奇数:"
for ((i=1; i<=10; i++)); doif [ $((i % 2)) -eq 0 ]; thencontinue # 是偶数则跳过fiecho $i
done
4.5 实战示例:文件管理小工具
综合本章所学,编写一个简单的文件管理工具,实现创建、删除、查看文件等功能:
#!/bin/bash
# 文件管理小工具
# 功能:创建文件、删除文件、查看文件内容echo "===== 文件管理工具 ====="
echo "1. 创建新文件"
echo "2. 删除文件"
echo "3. 查看文件内容"
echo "4. 退出"
echo "======================"read -p "请选择功能(1-4):" choicecase $choice in1)read -p "请输入要创建的文件名:" filename# 检查文件是否已存在if [ -e "$filename" ]; thenread -p "$filename 已存在,是否覆盖?(y/n):" overwriteif [ "$overwrite" != "y" ]; thenecho "取消创建"exit 0fifi# 创建文件touch "$filename"echo "$filename 创建成功";;2)read -p "请输入要删除的文件名:" filename# 检查文件是否存在if [ ! -e "$filename" ]; thenecho "错误:$filename 不存在"exit 1fi# 确认删除read -p "确定要删除 $filename 吗?(y/n):" confirmif [ "$confirm" = "y" ]; thenrm "$filename"echo "$filename 已删除"elseecho "取消删除"fi;;3)read -p "请输入要查看的文件名:" filename# 检查文件是否存在且是普通文件if [ ! -f "$filename" ]; thenecho "错误:$filename 不存在或不是普通文件"exit 1fi# 检查文件是否为空if [ -s "$filename" ]; thenecho "$filename 的内容:"echo "----------------------"cat "$filename"echo "----------------------"elseecho "$filename 是空文件"fi;;4)echo "谢谢使用,再见!"exit 0;;*)echo "错误:无效选项,请输入1-4"exit 1;;
esac
脚本说明:
- 使用
case
语句实现菜单选择 - 每个功能都有条件判断(文件是否存在、用户确认等)
- 用到了
touch
(创建文件)、rm
(删除文件)、cat
(查看文件)等基础Linux命令 - 有错误处理和用户交互,更贴近实际工具的使用场景
小结
本章学习了Shell中的条件判断与流程控制,主要内容包括:
- 条件判断基础:
test
命令和[]
表达式的使用,以及文件测试、字符串测试、数值测试三类条件运算符 - if语句:单分支(if)、双分支(if-else)、多分支(if-elif-else)的语法和应用场景,以及嵌套if的使用
- case语句:多值匹配的语法和适用场景,与if语句的选择建议
- 流程控制辅助命令:
exit
(终止脚本)、break
和continue
(循环控制,下章详细学习)
这些知识是让脚本具备"判断能力"的核心,通过合理使用条件判断,脚本可以根据不同情况做出不同响应,大大提高灵活性。
下一章我们将学习循环结构,它能让脚本重复执行命令,非常适合批量处理任务(如批量重命名文件、批量处理数据等)。