前面我们学了变量和运算,相当于给脚本准备了“原料”和“工具”。现在,我们要学习怎么指挥脚本根据不同的情况选择不同的执行路径,或者重复执行某些任务。这就是流程控制要做的事情,它能让你的脚本变得更加灵活和自动化。
1. 条件判断:如果这样,就那样 (if 语句)
(这部分与上一版本相同,包含 if
, if...else
, if...elif...else
, test
, [ ]
的讲解和示例)
生活中我们常做选择:“如果天冷,就多穿件衣服”。Shell 脚本里的 if
语句就是用来做这种判断的。它看某个条件满足不满足,然后决定接下来该执行哪些命令。
if
的基本样子:满足条件才执行
结构:
if [ 条件判断命令 ]; then
# 条件为真 (命令成功,退出码 0) 时执行这里的代码
要执行的命令...
fi
# fi 表示 if 语句块结束
示例:检查文件是否存在
#!/bin/bash
check_file="/etc/hosts"
echo "正在检查文件: $check_file"
if [ -f "$check_file" ]; then
echo "<font color='green'>文件 '$check_file' 找到了!</font> 👍"
fi
echo "检查完毕。"
if...else
:二选一,总得走一边
结构:
if [ 条件判断命令 ]; then
# 条件为真时执行
命令组A
else
# 条件为假 (命令失败,退出码非 0) 时执行
命令组B
fi
示例:判断数字是正是负(简化版)
#!/bin/bash
number=-5
echo "给定的数字是: $number"
if [ $number -gt 0 ]; then
echo "这是一个正数。"
else
echo "这不是一个正数 (可能是负数或零)。"
fi
if...elif...else
:多重条件,逐个尝试
结构:
if [ 条件1 ]; then
命令组1
elif [ 条件2 ]; then
命令组2
elif [ 条件3 ]; then
命令组3
else
默认命令组
fi
示例:根据分数评级
#!/bin/bash
score=75
echo "你的分数是: $score"
if [ $score -ge 90 ]; then
echo "优秀"
elif [ $score -ge 80 ]; then
echo "良好"
elif [ $score -ge 60 ]; then
echo "及格"
else
echo "需要加油"
fi
条件怎么写?认识 test
命令和 [ ]
(包含 test
和 [ ]
的讲解和示例,以及它们的常见判断类型和注意事项,同上)
… (此处省略与上一版本相同的 test
和 [ ]
内容) …
2. 循环结构:让任务跑起来 🏃♀️🔁
有时候我们需要重复执行一系列命令,比如处理一堆文件,或者等某个条件满足。这时候就要用到循环了。
for
循环:挨个处理列表中的成员 或 进行数值循环
for
循环非常强大,既能遍历一个列表,也能像 C 语言那样进行数值控制的循环。
格式 (遍历字符串列表):
for 变量名 in 字符串1 字符串2 ... ; do
# 处理 $变量名
循环体命令...
done
示例 (遍历字符串列表):
#!/bin/bash
echo "遍历颜色列表:"
for color in red green blue; do
echo "当前颜色是: $color"
done
格式 (遍历文件名 - 通配符):
for 变量名 in *.扩展名; do
# 处理 $变量名 (文件名)
循环体命令...
done
示例 (遍历文件名):
#!/bin/bash
echo "查找当前目录的 .log 文件:"
for logfile in *.log; do
if [ -f "$logfile" ]; then
echo "找到日志文件: $logfile"
else
echo "<font color='gray'>没有找到 .log 文件或通配符本身。</font>"
break
fi
done
格式 (遍历数字序列 - 花括号扩展):
for 变量名 in {开始..结束..步长}; do
# 处理 $变量名 (数字)
循环体命令...
done
示例 (遍历数字序列):
#!/bin/bash
echo "打印数字 1 到 3:"
for i in {1..3}; do
echo "数字: $i"
done
格式 (遍历命令输出):
for 变量名 in $(命令); do
# 处理 $变量名 (命令输出的每个词/行)
循环体命令...
done
示例 (遍历命令输出):
#!/bin/bash
echo "列出 /etc/ 下的目录 (简化示例):"
for dir_name in $(ls -p /etc | grep '/$' | sed 's/\///'); do
if [ -n "$dir_name" ]; then
echo "找到子目录: $dir_name"
fi
done
C 语言风格的 for
循环 (数值循环) 新增部分
这种格式特别适合需要精确控制循环次数、或者有明确起始值、结束条件和步进的数值循环场景。它的语法和 C 语言非常相似。
格式:
for (( 初始化表达式; 循环条件; 迭代表达式 )); do
# 循环体命令...
done
初始化表达式
: 在循环开始前执行一次,通常用来设置计数器的初始值 (例如i=1
)。循环条件
: 在每次循环开始前进行判断。如果条件为真 (计算结果非 0),就执行循环体;如果为假 (计算结果为 0),就退出循环。 (例如i<=10
)迭代表达式
: 在每次循环体执行完毕后执行,通常用来更新计数器的值 (例如i++
或i+=2
)。
示例:打印数字 0 到 4
#!/bin/bash
echo "使用 C 风格 for 循环打印 0 到 4:"
# 注意:双括号内变量名不需要加 $
for (( count=0; count<5; count++ )); do
echo "当前计数值是: $count"
done
示例:反向计数
#!/bin/bash
echo "反向计数从 5 到 1:"
for (( num=5; num>=1; num-- )); do
echo "倒计时: $num"
done
for ((...))
的优点:
- 语法清晰,对于有 C/Java 等背景的人来说很熟悉。
- 在双括号内进行的是算术运算,可以直接写
i++
,i<=10
等,非常方便。 - 比
while
循环配合外部计数器变量更紧凑。
while
循环:条件满足就一直做
(讲解和示例保持不变)
结构:
while [ 条件判断命令 ]; do
# 只要条件为真,就一直执行这里的命令
循环体命令
# 别忘了在循环体里通常需要有能改变条件的步骤,否则可能死循环!
done
示例:计数器,从 1 数到 5
#!/bin/bash
counter=1
while [ $counter -le 5 ]; do
echo "当前计数: $counter"
counter=$((counter + 1))
done
echo "循环结束!"
until
循环:直到条件满足才停
(讲解和示例保持不变)
结构:
until 条件判断命令; do
# 只要条件为假,就一直执行这里的命令
循环体命令
# 同样,需要有改变条件的步骤
done
示例:模拟等待某个文件出现
#!/bin/bash
file_to_wait="/tmp/ready.flag"
count=0
until [ -f "$file_to_wait" ]; do
count=$((count + 1))
echo "等待文件 $file_to_wait 出现... (第 $count 次检查)"
sleep 2
if [ $count -ge 5 ]; then
echo "等待超时!"
exit 1
fi
done
echo "文件 $file_to_wait 终于出现了!"
循环控制:break
和 continue
(讲解和示例保持不变)
break
💥: 立刻终止当前所在的整个循环。continue
⏭️: 立刻结束当前这一轮循环,直接开始下一次。
示例:
#!/bin/bash
echo "--- break 示例 ---"
for fruit in apple banana cherry date elderberry; do
echo "检查水果: $fruit"
if [[ "$fruit" == *"b"* ]]; then
echo "找到了带 'b' 的水果 ($fruit),停止查找!"
break
fi
done
echo "查找结束。"
echo "--- continue 示例 ---"
for i in {1..10}; do
if [ $((i % 2)) -eq 0 ]; then
continue # 跳过偶数
fi
echo "奇数: $i"
done
3. 分支选择:多条路选一条 (case 语句)
(讲解和示例保持不变)
结构:
case $变量 in
模式1)
命令组1
;;
模式2 | 模式3)
命令组2
;;
*) # 默认情况
默认命令组
;;
esac
示例:
#!/bin/bash
read -p "请输入一个命令 (start/stop/status): " action
echo "你输入的操作是: $action"
case $action in
start | begin)
echo "正在启动服务..."
;;
stop | halt)
echo "正在停止服务..."
;;
status | stat)
echo "正在检查服务状态..."
;;
*)
echo "<font color='red'>不认识的命令 '$action'!</font>"
;;
esac
echo "Case 处理完成。"
流程控制语句练习题 🧠✍️
(练习题和答案保持不变,因为新增的 for ((...))
循环主要用于数值循环,相关概念已在 while
循环题目中有所体现)
题目一:if
语句基础
❓ 写一个脚本,判断当前目录下是否存在一个名为 config.txt
的普通文件。如果存在,打印 “配置文件存在”;如果不存在,打印 “配置文件不存在”。
题目二:if...elif...else
❓ 写一个脚本,接收一个数字作为参数 ($1
)。如果数字大于 0,打印 “正数”;如果小于 0,打印 “负数”;如果等于 0,打印 “零”。
题目三:for
循环遍历
❓ 写一个 for
循环,打印出 apple
, banana
, cherry
这三个水果的名字,每个水果名字占一行。
题目四:while
循环计数
❓ 使用 while
循环,计算从 1 加到 10 的总和,并打印最终结果。
题目五:until
循环等待
❓ (概念题)while [ ! -f file.lock ]
和 until [ -f file.lock ]
这两种写法在逻辑上是否等价?它们都是用来做什么的?
题目六:循环控制 break
❓ 在一个从 1 循环到 20 的 for
循环中,如果遇到数字 13
,就立刻停止整个循环。写出这个循环。
题目七:循环控制 continue
❓ 写一个 for
循环,打印 1 到 10 之间所有不是 3 的倍数的数字。
题目八:case
语句选择
❓ 写一个 case
语句,根据变量 command
的值执行不同操作:如果值是 start
,打印 “启动服务”;如果是 stop
,打印 “停止服务”;如果是 restart
,打印 “重启服务”;对于其他任何值,打印 “未知命令”。
参考答案 ✅💡
答案一:
#!/bin/bash
config_file="config.txt"
if [ -f "$config_file" ]; then
echo "配置文件存在"
else
echo "配置文件不存在"
fi
答案二:
#!/bin/bash
number=$1 # 假设第一个参数是数字
# 实际脚本中应添加检查 $1 是否为空或非数字
if [ $number -gt 0 ]; then
echo "正数"
elif [ $number -lt 0 ]; then
echo "负数"
else
echo "零"
fi
答案三:
#!/bin/bash
for fruit in apple banana cherry; do
echo "水果: $fruit"
done
答案四:
#!/bin/bash
sum=0
counter=1
while [ $counter -le 10 ]; do
sum=$((sum + counter))
counter=$((counter + 1))
done
echo "1 加到 10 的总和是: $sum" # 输出 55
答案五:
是的,它们在逻辑上是等价的 ✅。两者都是用于等待某个条件(文件存在)成立。
答案六:
#!/bin/bash
for i in {1..20}; do
echo "当前数字: $i"
if [ $i -eq 13 ]; then
echo "遇到 13,停止循环!"
break # 跳出循环
fi
done
echo "循环结束。"
(或者用 C 风格 for 循环实现同样效果):
#!/bin/bash
for (( i=1; i<=20; i++ )); do
echo "当前数字: $i"
if [ $i -eq 13 ]; then
echo "遇到 13,停止循环!"
break
fi
done
echo "循环结束。"
答案七:
#!/bin/bash
echo "打印非 3 的倍数 (1-10):"
for i in {1..10}; do
if [ $((i % 3)) -eq 0 ]; then # 如果是 3 的倍数
continue # 跳过本次循环
fi
echo $i
done
(或者用 C 风格 for 循环实现):
#!/bin/bash
echo "打印非 3 的倍数 (1-10):"
for (( i=1; i<=10; i++ )); do
if [ $((i % 3)) -eq 0 ]; then
continue
fi
echo $i
done
答案八:
#!/bin/bash
command="start" # 假设 command 变量的值是 start
case $command in
start)
echo "启动服务"
;;
stop)
echo "停止服务"
;;
restart)
echo "重启服务"
;;
*)
echo "未知命令: $command"
;;
esac