Shell 编程
Shell 编程,也称为 shell 脚本,是 Linux作系统不可或缺的一部分。shell 脚本实质上是系统 shell 执行的程序。虽然它可能不如 C 或 C++ 等编译语言强大,但 shell 编程对于管理级任务、自动执行重复性任务和系统监控非常有效。
大多数 Linux 发行版都带有 bash (Bourne Again Shell) 作为默认 shell,它不仅是一个出色的命令行 shell,也是一种出色的脚本语言。Shell 脚本通常是在文本编辑器中编写的,然后可以直接从 Linux 命令行运行。
bash shell 脚本的简单示例:
#!/bin/bash
# My first script
echo "Hello, World!"
'echo' 命令将其参数(在本例中为 “Hello, World!”)打印到终端。
🍳 Shell编程就像写菜谱
核心概念: 把复杂的厨房工作写成步骤清单(脚本),让电脑这个"智能厨师"自动执行
📝 基础菜谱结构(脚本示例)
#!/bin/bash
# 自动备份菜谱
echo "开始备份..."
cp -v /菜谱/*.txt /备份/ # 复制所有菜谱到备份目录
echo "备份完成!记得尝新菜哦~"
逐行解释:
-
#!/bin/bash
→ 指定用Bash厨具(解释器) -
# 注释
→ 给厨师的提醒笔记(不执行) -
echo
→ 厨房喇叭(输出信息) -
cp -v
→ 复制文件并语音播报进度
🛠️ 常用厨房工具(命令)
工具 | 功能 | 示例 |
---|---|---|
变量 | 食材储物罐 | 菜名="红烧肉" |
循环 | 批量处理食材 | for 肉 in 猪肉 牛肉; do 焯水 $肉; done |
条件判断 | 火候控制 | if [ 温度 -gt 100 ]; then 调小火; fi |
函数 | 预制调料包 | 切菜() { echo "正在切$1..." } |
🌰 实用菜谱案例
1️⃣ 自动清理过期菜品(删除旧文件)
#!/bin/bash
# 删除7天前的日志
find /var/log -name "*.log" -mtime +7 -exec rm {} \;
echo "过期日志清理完毕!"
这个命令的作用是在 /var/log 目录下查找所有修改时间超过 7 天的 .log 文件,并删除它们。
具体解析:
find /var/log —— 在 /var/log 目录及其子目录中查找文件。
-name "*.log" —— 只匹配 .log 结尾的文件。
-mtime +7 —— 找出最后修改时间超过 7 天的文件(+7 表示“7 天前及更早”)。
-exec rm {} \; —— 对找到的文件执行 rm 命令,即删除这些文件({} 代表找到的文件,\; 表示 -exec 命令的结束)。
简单来说,这个命令就是清理 7 天前的日志文件,防止日志堆积占用磁盘空间。
2️⃣ 智能煮饭定时器(计划任务)
#!/bin/bash
煮饭时间=40 # 分钟
echo "⏰ 开始煮饭,设定${煮饭时间}分钟"
sleep ${煮饭时间}m && speak "饭煮好啦!"
m 表示“分钟”,例如 sleep 30m 就会让程序暂停 30 分钟。
3️⃣ 食材库存检查(监控磁盘)
#!/bin/bash
使用率=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
if [ $使用率 -gt 90 ]; then
echo "⚠️ 厨房快满了!当前使用率: ${使用率}%"
else
echo "✅ 存储空间充足: ${使用率}%"
fi
使用率=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
df -h /:查看 / 目录的磁盘使用情况,-h 让结果以人类可读的格式显示(比如 GB、MB)。
awk 'NR==2 {print $5}':提取第二行的第五列,通常是使用率(比如 85%)。
tr -d '%':去掉 % 符号,使它变成纯数字(方便后续比较)。
if [ $使用率 -gt 90 ]; then
判断使用率是否大于 90%(如果 使用率 超过 90,就表示存储快满了)。
echo "⚠️ 厨房快满了!当前使用率: ${使用率}%"
如果磁盘使用率超过 90%,就打印警告消息 “⚠️ 厨房快满了!”(这里把磁盘比作“厨房”很有趣😆)。
else echo "✅ 存储空间充足: ${使用率}%"
如果使用率低于 90%,就打印**“✅ 存储空间充足”**,说明还有足够的磁盘空间。
🔧 厨房安全须知(最佳实践)
-
先试菜再上桌:用
bash -x 脚本.sh
调试 -
小心刀具:用
set -e
遇到错误立即停止 -
标记食材:变量用
${变量}
包裹更安全 -
定期维护:用
shellcheck
检查脚本语法
💡 高级烹饪技巧
-
食材组合(管道操作):
# 找出所有图片并打包 find . -name "*.jpg" | xargs tar -czvf photos.tar.gz
find . -name "*.jpg" 在当前目录(.)及其所有子目录中,查找扩展名为 .jpg 的文件。 | xargs tar -czvf photos.tar.gz xargs 是一个构造命令参数列表并执行命令的工具。 在 find . -name "*.jpg" | xargs tar -czvf photos.tar.gz 这个命令中,find 找到的 .jpg 文件名会通过 | 传递给 xargs,然后 xargs 把这些文件名作为参数拼接到 tar -czvf photos.tar.gz 后面,最终执行 tar 命令进行压缩。 为什么要用 xargs? find 默认会把所有匹配的文件名逐行输出,但 tar 需要文件名作为参数放在同一行。 xargs 自动把多行输出转换成一行参数列表,避免 tar 处理不了文件名的问题。 比 find ... -exec 更高效,因为 xargs 只执行一次 tar,而 -exec 默认对每个文件执行一次(除非用 +)。 |(管道符)把 find 找到的 .jpg 文件列表传递给 xargs,然后交给 tar 处理。 tar -czvf photos.tar.gz c(create):创建一个新的压缩包。 z(gzip):使用 gzip 进行压缩。 v(verbose):显示打包过程,列出正在添加的文件。 f photos.tar.gz:指定输出文件名 photos.tar.gz。
-
万能锅具(Here Document):
cat <<EOF > 菜单.txt 今日特价: 1. ${招牌菜} 2. 时令蔬菜 EOF
-
秘密配方(环境变量):
export 秘制酱料="酱油:糖=3:1" ./烹饪脚本.sh # 子脚本也能使用这个配方
总结: Shell编程就像掌握了一套智能厨房管理系统,通过:
-
📜 编写自动化菜谱(脚本)
-
⚡ 批量处理食材(文件操作)
-
🔔 设置烹饪提醒(定时任务)
-
🛡️ 监控厨房安全(系统监控)
让你从重复劳动中解放,成为高效能的"数字大厨"! 👨🍳💻
Linux 上 Shell 编程中的文字
在 Linux 环境中,shell 脚本是系统作和应用程序开发的重要组成部分。shell 脚本的一个关键方面是使用 Literals。在计算机科学和 shell 编程中,术语“literal”是指在源代码中表示固定值的表示法。在 shell 脚本中,这些固定值可以包括字符串文本、数字文本或布尔值。在阅读和理解现有脚本或编写新脚本时,了解如何以及何时使用这些文本至关重要。下面列出了 Linux 下的一些基本 shell 脚本文字:
字符串文字:可以通过将文本括在单引号或双引号之间来定义它们。例如,'Hello, world!' 或 “Hello, world!”。
数字文本:它们表示数字序列。例如,25、100 或 1234。
布尔文本:在大多数 Linux shell 脚本中,1 表示 true,0 表示 false。
请注意您使用的文本类型,因为它会显著影响您的脚本、代码的可读性及其整体功能。
#!/bin/bash
# Example of literals in shell script
StringLiteral="This is a string literal"
NumericLiteral=125
echo $StringLiteral
echo $NumericLiteral
在此示例中, and 是文本 和 用于打印它们。StringLiteral
NumericLiteral
e cho
永远记住,在 Linux 中编写 shell 脚本时,对文本的良好理解是基础。
Shell脚本中的“文字”(字面量)其实就是你直接写出来的固定值,就像我们说话时直接提到的具体内容。比如:
1️⃣ 字符串字面量(文字)
就是直接写出来的文字,像把一句话装进盒子里。有两种包装方式:
- 单引号盒子 ' ':盒子里的内容会完全保持不变
name='小明 $100' # $符号和数字都会原样保存
- 双引号盒子 " ":盒子里的内容可以“透气”,能识别里面的变量和特殊符号
money=100 name="小明有 ¥$money" # 输出:小明有 ¥100
举个栗子🌰:
echo '今天是$HOSTNAME' # 输出:今天是$HOSTNAME
echo "今天是$HOSTNAME" # 输出:今天是你的电脑名称
2️⃣ 数字字面量(文字)
直接写数字,但要注意 Shell默认所有内容都是字符串,想计算需要特殊处理:
age=25 # 直接写数字
sum=$((10+15)) # 需要$(( ))才能计算
错误示范❌:
result=10+20 # 这不会计算,会被当作字符串"10+20"
3️⃣ 布尔字面量(文字)
Shell用 命令执行是否成功 表示真假:
- 0 表示真(True)→ 因为命令执行成功时返回0
- 非0 表示假(False)→ 不同的数字代表不同的错误类型
举个栗子🌰:
if grep "hello" file.txt; then
# 如果找到"hello"(返回0),执行这里
else
# 如果没找到(返回1),执行这里
fi
📝 重点总结
类型 | 示例 | 特点说明 |
---|---|---|
字符串 | name="小明" | 单引号严格,双引号灵活 |
数字 | count=42 | 直接写数字,计算需特殊符号 |
布尔 | 成功返回0 | 与常见编程语言相反,特别注意 |
小练习✍️:
# 观察以下代码的区别
num=5+3
echo $num # 输出什么?
echo $((num)) # 输出什么?
echo $(($num)) # 又会输出什么?
通过理解这些直接写出来的“文字”,你就能更自如地控制Shell脚本的行为了!
总结
代码 | 解析 | 输出 |
---|---|---|
echo $num | num="5+3" ,所以直接输出 5+3 | 5+3 |
echo $((num)) | num 里是字符串 "5+3" ,不能计算,会报错 | 报错 |
echo $(($num)) | $(("5+3")) ,Bash 解析并计算 5+3 | 8 |
Linux 上 Shell 编程中的变量
在 Linux 上的 Shell 编程上下文中,变量是可以存储系统数据或用户定义数据的字符串。它是一个符号名称,分配给一定量的存储空间,该空间量可以在程序执行期间更改其值。变量在任何编程范式中都起着至关重要的作用,shell 脚本也不例外。
变量分为两大类:系统变量和用户定义的变量。系统变量由 Linux 系统本身创建和维护。示例包括 PATH、HOME 和 PWD。另一方面,用户定义的变量由用户创建和控制。
shell 脚本中的变量由 '=' (等于) 运算符定义,可以通过在变量名称前加上 '$' (美元) 符号来检索该值。
# Create a User-Defined Variable
MY_VARIABLE="Hello World"
# Print the value of the Variable
echo $MY_VARIABLE # Output: Hello World
🏷️ Shell变量就像厨房的储物罐
核心概念: 给不同的食材(数据)贴上标签(变量名),方便随时取用和修改
🧂 变量使用基础
食材="盐" # 贴上标签(变量赋值)
echo "请加${食材}" # 使用时要加${}(防止混淆)
=> 请加盐
🥫 变量分类(储物柜类型)
变量类型 | 特点 | 示例 | 查看方式 |
---|---|---|---|
系统变量 | 厨房自带,全大写 | $HOME (你的厨房位置) | env 或 printenv |
用户变量 | 厨师自定义,建议小写 | $菜谱 | echo $菜谱 |
环境变量 | 子厨房也能用 | $PATH (工具位置) | export 显示 |
局部变量 | 只在当前厨房有效 | 函数内定义的变量 | 无法直接查看 |
🛠️ 变量使用技巧
1️⃣ 避免标签混淆
# 错误示范(标签贴错位置)
数量=5
echo "需要$数量斤肉" # 输出:需要5斤肉(正确)
echo "需要$数量斤肉" # 输出:需要斤肉($数量斤被误解析)
错误示范的代码,问题出在哪里?
$数量
代表变量数量
,它的值是5
。- 但
$数量斤
在bash
里会被当作一个新的变量数量斤
,而不是$数量
加上斤
。 - 如果
数量斤
这个变量没有被定义,Bash 会解析为空字符串,导致"需要$数量斤肉"
变成"需要斤肉"
。
2️⃣ 设置只读调料瓶
readonly 秘方="酱油3勺"
秘方="醋2勺" # 报错:秘方是只读变量
3️⃣ 让子厨房继承变量
export 公用调料="胡椒粉" # 其他厨房脚本也能使用
🌰 实战菜谱示例
自动备份菜谱
#!/bin/bash
备份目录="/厨房/菜谱备份"
今日菜单=$(date +%Y%m%d).txt
echo "正在备份今日菜单..."
cp 今日菜单.txt "${备份目录}/${今日菜单}"
echo "备份完成!存放于:${备份目录}/${今日菜单}"
⚠️ 厨房安全须知
-
标签规则:
-
不能以数字开头:
1号罐=盐
❌ →罐1=盐
✅ -
避免特殊字符:
调料-油=花生油
❌ →调料油=花生油
✅
-
-
赋值禁忌:
食材 = "盐" # 等号两边不能有空格 ❌ 食材="盐" # 正确 ✅
-
空值处理:
echo "缺货: ${库存:-无}" # 库存为空时显示"无"
💡 高级调味技巧
-
字符串切割:
完整菜名="红烧肉.jpg" echo ${完整菜名%.*} # 输出:红烧肉(去掉后缀)
-
变量计算:
鸡蛋=5 鸡蛋=$((鸡蛋 + 3)) # 现在有8个鸡蛋
-
命令结果存变量:
当前温度=$(sensors | grep 'CPU temp' | awk '{print $3}') echo "CPU温度: ${当前温度}"
总结: Shell变量就像智能厨房的标签系统:
-
🏷️ 命名:用有意义的标签方便管理
-
🔒 作用域:分清私房调料和公共调料
-
🛡️ 保护:重要配方设为只读
-
🔄 灵活使用:支持动态修改和计算
掌握这些技巧,让你的Shell脚本像米其林大厨一样高效有序! 👨🍳💻
export
的作用是让变量在子进程中也可用。
✅ 变量在 export
之后仍然是当前 shell 的变量,但子进程也能访问。
✅ export
变量在新终端不会自动生效,除非写入 ~/.bashrc
或 ~/.bash_profile
。
✅ unset
可以删除 export
变量。
💡 一句话总结:export
让变量变成环境变量,这样它可以在当前 shell 及所有子进程中使用,但不会影响新的终端,要让它永久生效,必须写入 ~/.bashrc
或 ~/.bash_profile
。🚀
循环
shell 编程中的循环是一个基本概念,它允许根据给定条件一遍又一遍地执行某个代码块。它们对于自动执行重复性任务至关重要,从而提高编码过程的效率且不易出错。
在 Linux 中,shell 脚本通常使用三种类型的循环 - for、while 和 until。
for
Loop 迭代项目列表并对每个项目执行作。while
只要控制条件保持 true,loop 就会执行命令。until
loop 运行命令,直到 control condition 变为 true。
以下是 bash/shell 中的一个简单的 for 循环示例:
for i in 1 2 3
do
echo "$i"
done
这将输出:
1
2
3
这只是 Linux 中 shell 编程中循环的表面。如果明智地使用这些结构,可以增强您的脚本并为有效的脚本编写和自动化开辟许多领域。
好的,我来用最生活化的方式解释 Shell 中的循环,就像教朋友做菜时的步骤说明:
循环就像「重复做某件事的规则」
想象你要做以下事情:
1️⃣ 按清单买水果 → 用 for
循环
2️⃣ 洗碗直到洗完 → 用 while
循环
3️⃣ 等快递直到门铃响 → 用 until
循环
1️⃣ **for
循环:按固定清单办事**
场景:你有一张购物清单,要依次买上面的每样东西
语法:
for 物品 in 苹果 香蕉 橘子 # 清单可以是数字/文件名等
do
echo "现在买:$物品" # 对每个物品执行操作
done
示例:
for fruit in 苹果 香蕉 橘子
do
echo "买了 $fruit"
done
输出👇
买了 苹果
买了 香蕉
买了 橘子
小技巧:快速生成数字序列
for i in {1..5} # 输出1到5
for i in $(seq 1 2 10) # 输出1 3 5 7 9(步长2)
2️⃣ **while
循环:只要条件成立,就一直做**
场景:洗碗时,只要还有脏碗,就继续洗
语法:
while [ 条件 ] # 条件为真(返回0)时执行
do
echo "正在洗碗..."
更新条件 # 必须修改条件,否则无限循环!
done
示例(倒计时3秒):
counter=3
while [ $counter -gt 0 ] # -gt 表示大于
do
echo "倒计时:$counter"
counter=$((counter-1)) # 修改条件变量
done
输出👇
倒计时:3
倒计时:2
倒计时:1
⚠️ 重点:条件里的方括号 [ ]
两边必须有空格!
3️⃣ **until
循环:一直做,直到条件成立**
场景:等快递时,每隔5分钟查一次,直到快递到了
语法:
until [ 条件 ] # 条件为假(返回非0)时执行
do
echo "快递还没到"
更新条件
done
示例(从5减到0停止):
counter=5
until [ $counter -eq 0 ] # -eq 表示等于
do
echo "剩余次数:$counter"
counter=$((counter-1))
done
输出👇
剩余次数:5
剩余次数:4
剩余次数:3
剩余次数:2
剩余次数:1
三种循环对比表
循环类型 | 执行时机 | 常用场景 | 类比 |
---|---|---|---|
for | 按已知清单逐个处理 | 遍历文件、数字序列 | 按购物清单买东西 |
while | 只要条件成立就执行 | 不确定次数的重复操作 | 洗碗直到没有脏碗 |
until | 直到条件成立才停止 | 等待某个状态变化 | 等快递直到门铃响 |
小练习✍️:
猜猜这段代码会输出什么?
for num in 3 1 4
do
while [ $num -gt 0 ]
do
echo -n "$num "
num=$((num-1))
done
done
答案:
3 2 1 1 4 3 2 1
(解释:外层for
循环依次处理3→1→4,每个数字内层while
循环减到0停止)
Shell 编程中的条件语句
Linux Shell 编程中的条件语句允许脚本根据条件做出决策。这些是任何编程语言不可或缺的一部分,就像 C、Python、JavaScript 等其他语言一样,Linux Shell 也提供条件语句。条件语句可以定义为 shell 脚本的组成部分,它根据给定的条件引导解释器进入正确的执行路径。
在 shell 中,用于条件语句的主要命令是 、 、 (else if) 和 。这些命令用于基于条件测试结果的流程控制,条件测试可以评估字符串变量、算术测试或流程状态的值。if
elif
else
以下是它们工作原理的简单说明:
#!/bin/sh
a=10
b=20
if [ $a -lt 20 ]
then
echo "a is less than b"
elif [ $a -gt 20 ]
then
echo "a is greater than b"
else
echo "a is equal to b"
fi
在上面的脚本中,正在检查语句内的条件。如果条件为 ,则执行语句中的代码块,否则,它将移动到条件,依此类推。如果这些条件都不满足,则将执行语句中的代码块。if
true
if
elif
else
🌦️ Shell条件语句就像天气决策指南
核心概念: 根据不同的天气条件(判断条件),决定今天要做什么(执行不同代码块)
📝 基础天气预报结构
#!/bin/bash
温度=25
if [ $温度 -lt 10 ]; then
echo "❄️ 穿羽绒服!"
elif [ $温度 -lt 20 ]; then
echo "🍂 穿外套吧"
else
echo "☀️ 短袖出门~"
fi
🌈 天气判断要素解析
条件符号 | 含义 | 示例 | 生活类比 |
---|---|---|---|
-lt | 小于(<) | [ $温度 -lt 30 ] | 温度低于30度 |
-gt | 大于(>) | [ $电量 -gt 20 ] | 电量高于20% |
-eq | 等于(==) | [ $星期 -eq 1 ] | 周一 |
-ne | 不等于(!=) | [ $天气 != "雨" ] | 不是雨天 |
-le | 小于等于(<=) | [ $年龄 -le 18 ] | 未成年人 |
-ge | 大于等于(>=) | [ $身高 -ge 120 ] | 身高120cm以上 |
🌰 真实生活场景案例
1️⃣ 文件备份检查
if [ -f "/data/备份.zip" ]; then
echo "✅ 备份存在"
else
echo "⚠️ 警告:未找到备份文件!"
exit 1
fi
2️⃣ 用户输入判断
read -p "今天要带伞吗?(y/n): " 选择
if [ "$选择" = "y" ]; then
echo "🌂 记得放包里"
elif [ "$选择" = "n" ]; then
echo "☀️ 享受晴天吧"
else
echo "输入错误,请重新选择"
fi
3️⃣ 多条件组合
if [ $时间 -gt 6 -a $时间 -lt 18 ]; then
echo "现在是白天"
elif [ $时间 -lt 6 -o $时间 -ge 18 ]; then
echo "现在是夜晚"
fi
⚠️ 天气预测注意事项
-
空格敏感 → 方括号两边必须有空格
❌错误:[$温度<25]
✅正确:[ $温度 -lt 25 ]
-
变量防护 → 用双引号包裹变量防止空值
❌危险:[ $文件名 == "test" ]
✅安全:[ "$文件名" == "test" ]
-
逻辑运算符
-
-a
→ 且(and) -
-o
→ 或(or)
-
💡 高级天气预测技巧
-
文件测试:
if [ -d "/data" ]; then # 检查是否是目录 echo "找到数据仓库" fi
-
字符串比较:
if [ "$OSTYPE" = "linux-gnu" ]; then echo "Linux系统" fi
$OSTYPE 是一个系统变量
OSTYPE 变量存储了当前操作系统的类型,比如:
在 Linux 上,echo $OSTYPE 通常输出 linux-gnu
在 macOS 上,echo $OSTYPE 通常输出 darwin
在 Windows(WSL)上,可能输出 linux-gnu
-
数学运算:
if (( 温度 > 30 )); then # 双括号支持数学表达式 echo "高温预警!" fi
总结: Shell条件语句就像智能天气预报系统,通过:
-
🌧️ 基础判断 → 单条件决策
-
⛅ 多级判断 → 处理复杂天气变化
-
🌈 组合条件 → 应对特殊天气组合
让你的脚本具备智能决策能力!
在 Linux 下进行 Shell 编程调试
Linux 是一个强大而灵活的作系统,许多开发人员和系统管理员都喜欢它的多功能性和强大功能。特别是,Linux 中的 shell 编程允许您高效地自动执行任务和管理系统。但是,鉴于 shell 脚本的复杂性,调试是处理错误和提高代码性能的一项基本技能。
在 shell 脚本中遇到问题时,您可以在 Linux 环境中使用多种调试工具。这些有助于检测、跟踪和修复 shell 脚本中的错误或错误。其中一些调试工具包括 bash shell 的 (or ) 选项,这些选项允许执行跟踪。其他工具(如 , command),甚至利用外部调试工具(如 )也可能非常有效。-x
-v
trap
set
shellcheck
考虑使用 -x 选项打开 shell 脚本以进行执行跟踪,如下所示:
#!/bin/bash -x
或者,您可以直接从命令行在调试模式下运行脚本。
bash -x script.sh
这些调试工具和选项可以极大地帮助您使脚本更加防错和高效。
我来用生活中「修理东西」的场景,帮你理解 Shell 脚本调试的套路:
调试就像修漏水的水管
想象你家的水管漏水,但不知道哪里破了。你会:
1️⃣ 看水从哪流出来 → 用 -x
选项看脚本执行流程
2️⃣ 分段检查水管 → 用 set -x
和 set +x
局部调试
3️⃣ 用检测仪找裂缝 → 用 shellcheck
工具提前发现隐患
1️⃣ 「全程监控」模式:bash -x
或 #!/bin/bash -x
作用:像摄像头一样记录脚本每一步的执行过程
示例:调试一个打印数字的脚本
#!/bin/bash -x # 关键在这里!
for i in 1 2 3
do
echo "Number: $i"
done
输出👇(带 +
的行是调试信息)
+ for i in 1 2 3
+ echo 'Number: 1'
Number: 1
+ for i in 1 2 3
+ echo 'Number: 2'
Number: 2
+ for i in 1 2 3
+ echo 'Number: 3'
Number: 3
适用场景:快速定位脚本卡在哪里,观察变量值变化
2️⃣ 「局部放大镜」调试:set -x
和 set +x
作用:只检查脚本的某一段代码,避免信息过多
示例:只监控循环部分
#!/bin/bash
echo "开始执行"
set -x # 从这里开始记录
for i in {1..2}
do
echo "第 $i 次循环"
done
set +x # 到这里停止记录
echo "执行结束"
输出👇
开始执行
+ for i in '{1..2}'
+ echo '第 1 次循环'
第 1 次循环
+ for i in '{1..2}'
+ echo '第 2 次循环'
第 2 次循环
+ set +x
执行结束
适用场景:长脚本中重点排查某段代码
3️⃣ 「智能安检仪」:shellcheck
工具
作用:像机场安检机,提前发现脚本中的隐患
安装:sudo apt install shellcheck
(Debian/Ubuntu)
使用:
shellcheck 你的脚本.sh
示例:检查一个未引用的变量
# 问题脚本 test.sh
name=Kk
if [ $name == "Kk" ] # 这里$name没加引号!
then
echo "YES"
fi
运行 shellcheck test.sh
会提示:
Line 2: if [ $name == "Kk" ]
^-- SC2077: 建议给$name加双引号,避免空格导致错误
优点:提前发现语法错误、不安全代码
4️⃣ 「紧急刹车」调试:trap
命令
作用:在脚本出错时自动触发调试(类似汽车碰撞时弹气囊)
示例:任何命令失败时打印错误行
#!/bin/bash
trap 'echo "出错啦!在行号: $LINENO"' ERR # 捕获错误信号
echo "第一步正常"
ls 不存在的文件 # 这里会报错
echo "第二步正常" # 不会执行到这里
输出👇
第一步正常
ls: 无法访问'不存在的文件': 没有那个文件或目录
出错啦!在行号: 4
适用场景:捕捉脚本崩溃前的状态
5️⃣ 「人工检查」技巧:echo
打印变量
作用:像用便签纸标记重要位置,手动确认变量值
示例:检查循环变量
for user in "Tom" "Jerry"
do
echo "调试点:当前用户是 $user" >&2 # >&2 表示输出到错误流
# ...其他代码...
done
输出👇
调试点:当前用户是 Tom
调试点:当前用户是 Jerry
优点:简单粗暴有效,适合快速测试
调试工具对比表
方法 | 适用阶段 | 优点 | 类比 |
---|---|---|---|
bash -x | 运行时调试 | 全局跟踪执行流程 | 水管全段摄像头 |
set -x | 运行时调试 | 局部精准检查 | 手电筒照特定区域 |
shellcheck | 写代码时 | 提前预防错误 | 施工前的设计检查 |
trap | 运行时错误处理 | 捕捉崩溃瞬间状态 | 汽车黑匣子 |
echo | 任何情况 | 灵活直观 | 贴便签做标记 |
调试小贴士💡:
- 遇到无限循环时,按
Ctrl + C
强制终止 - 变量赋值后立即用
echo
确认值是否正确 - 复杂脚本用
#
注释掉部分代码,分段测试 - 错误信息别怕,先看行号,再检查附近代码
练习:写一个错误脚本(比如计算 1+2 但结果错误),用以上方法调试并修复它!