Linux Shell 编程基础学习与实践作业
- 一、前置学习内容
- 二、基础作业:查询指定网口的启用状态
- [三、基础作业:列出所有活跃网口及 IP 地址](#三、基础作业:列出所有活跃网口及 IP 地址)
- 四、进阶作业:网口流量实时监控(简易版)
- 五、进阶作业:网口状态异常告警
- 六、综合作业:网口信息汇总报告生成
一、前置学习内容
1.基础框架和权限
第一步:创建脚本文件
#创建目录(如果没有的话)
mkdir -p ~/shell_demo
#进入目录
cd ~/shell_demo
#用vim创建并编辑脚本文件(如果不会vim,也可以用nano,命令是nano first_script.sh)
vim first_script.sh
第二步:写入基础脚本内容
#!/bin/bash
#这是我的第一个Shell脚本(单行注释)
#功能:演示最基础的变量定义和输出
#定义变量(注意:等号前后不能有空格!)
greeting="你好,Shell编程!"
today="2026年1月18日"
#输出变量内容(用echo,引用变量加$)
echo "---------- 基础脚本演示 ----------"
echo g r e e t i n g e c h o " 今天的日期: greeting echo "今天的日期: greetingecho"今天的日期:today"
#也可以用 变量名的方式引用(更规范) e c h o " 再次输出: {变量名}的方式引用(更规范) echo "再次输出: 变量名的方式引用(更规范)echo"再次输出:{greeting}"
第三步:保存并退出 vim
• 按Esc键(确保退出编辑模式)
• 输入:wq(意思是保存(write)并退出(quit)),按回车
第四步:赋予执行权限并运行
#赋予脚本执行权限(+x就是添加可执行权限)
chmod +x first_script.sh
#运行脚本(用相对路径,当前目录下的脚本)
./first_script.sh
PS:
你可以把 Linux 系统想象成一个严格的图书馆 / 档案室:
• 你创建的first_script.sh文件,默认就是一张 "写满字的纸"(纯文本文件),系统只允许你「看(读权限 r)」、「改(写权限 w)」,但不允许你把这张纸当成 "指令单" 去执行;
• chmod +x 就相当于给这张纸盖了一个「"可执行" 公章」------ 告诉图书馆管理员(系统内核):"这张纸不是普通的笔记,是可以照做的指令清单,允许按上面的内容执行"。
第五步:输出结果

2. read 输入功能+ if 条件判断
步骤 1:创建新脚本 input_demo.sh
vim input_demo.sh
步骤 2:写入脚本内容
#!/bin/bash
#功能:演示read读取用户输入
#重点:-p 参数可以直接加提示语,不用单独echo
#用read读取用户输入的名字,-p 后面跟提示文字
read -p "请输入你的名字:" username
#输出欢迎语,拼接变量
echo "="
echo " 你好呀,${username}!"
echo " 恭喜你掌握了read输入功能~"
echo "="
#进阶:读取多个输入(比如名字+年龄)
read -p "再输入你的名字和年龄(用空格隔开):" name age
echo "你输入的名字: n a m e ,年龄: {name},年龄: name,年龄:{age}岁"
步骤 3:保存退出 + 加权限 + 运行
按老规矩操作:
- Esc → :wq 保存退出 vim
- 加执行权限:chmod +x input_demo.sh
- 运行脚本:./input_demo.sh
步骤 4:结果

步骤 1:创建脚本 if_demo.sh
vim if_demo.sh
步骤 2:写入脚本内容
#!/bin/bash
#功能:演示if-else条件判断(数字判断+范围判断)
#第一步:读取用户输入的年龄
read -p "请输入你的年龄:" age
#第二步:先判断输入的是否是纯数字(新手必学的正则判断)
#[[ 表达式 ]] 是更友好的判断方式,=~ 是正则匹配
if ! [[ " a g e " = [ 0 − 9 ] + age" =~ ^[0-9]+ age"= [0−9]+ ]]; then
echo "❌ 错误!你输入的不是有效数字,请重新运行脚本输入数字。"
退出脚本,返回错误码1(表示执行失败)
exit 1
fi
#第三步:判断年龄范围(用 -lt(小于)、-ge(大于等于) 等数字判断符)
echo "✅ 你输入的年龄是: a g e 岁 " i f [ " age 岁" if [ " age岁"if["age" -lt 18 ]; then
echo "👉 你还是未成年人,要注意保护自己哦~"
elif [ "KaTeX parse error: Expected 'EOF', got '&' at position 15: age" -ge 18 ] &̲& [ "age" -lt 60 ]; then
echo "👉 你是成年人啦,要对自己的选择负责~"
else
echo "👉 你已经到了退休的年纪,好好享受生活吧~"
fi
PS:
fi 就是 if 的反向拼写,唯一作用:标记 if 条件判断结构的结束,必须和 if 成对出现。
步骤 3:保存退出 + 加权限 + 运行
• Esc → :wq 保存退出
• 加权限:chmod +x if_demo.sh
• 运行脚本,测试 4 种场景:
• 场景 1:输入非数字(比如abc)→ 报错提示
• 场景 2:输入 16 → 提示未成年
• 场景 3:输入 30 → 提示成年
• 场景 4:输入 65 → 提示退休

3. case判断-网络命令 + 文本过滤脚本
步骤 1:创建脚本 nic_status.sh
vim nic_status.sh
步骤 2:写入脚本内容
#!/bin/bash
#功能:网卡状态查询工具(case语句+网络命令实战)
#第一步:打印菜单,让用户选择操作
echo "==================== 网卡查询工具 "
echo "1. 查看所有网卡列表"
echo "2. 检查指定网卡是否启动"
echo "3. 退出"
echo "=================================="
read -p "请输入你的选择(1/2/3):" choice
#第二步:用case处理多值选择(适合固定选项的场景)
case "$choice" in
查所有网卡列表:ip link show 取第2列(网卡名),grep -v 排除回环网卡lo
echo -e "\n📋 系统所有网卡列表(排除回环网卡):"
ip link show | awk '{print $2}' | grep -v "lo:" | cut -d ":" -f1
;; # 每个分支结束必须加;;
2)
# 查指定网卡状态
read -p "请输入要检查的网卡名(如ens33/eth0):" nic
# 判断网卡是否存在:ip link show 网卡名 重定向输出到黑洞(不显示)
if ip link show "$nic" > /dev/null 2>&1; then
# 过滤网卡是否UP(grep -q 静默过滤,只返回结果不输出)
if ip addr show "$nic" | grep -q "UP"; then
echo -e "\n✅ 网卡$nic 状态:已启动(UP)"
else
echo -e "\n❌ 网卡$nic 状态:已关闭(DOWN)"
fi
else
echo -e "\n❌ 错误:网卡$nic 不存在!"
fi
;;
3)
echo -e "\n👋 脚本退出,再见!"
exit 0 # 正常退出
;;
*) # 匹配所有未定义的输入(比如4/abc)
echo -e "\n❌ 错误:输入无效,请输入1/2/3!"
exit 1
;;
esac # case结束标记(case反过来)
步骤3:逐个分析

1:先看 ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether 00:0c:29:xx:xx:xx brd ff:ff:ff:ff:ff:ff
ip link show | awk -F ':' '/^ [0-9]+:/ {print $2}' | grep -v "lo" | tr -d ' '
咱们拆解一下这个精准版:
- awk -F ':':把每一行按冒号 : 分割成多列。
- 2/^ [0-9]+:/:只匹配以 "数字 + 冒号" 开头的行(这些行才是网卡的定义行,比如 1: lo:、2: ens33:)。
- {print $2}:提取分割后的第二列,也就是网卡名(比如 lo、 ens33)。
- grep -v "lo":排除回环网卡。
- tr -d ' ':去掉网卡名前面的空格,得到干净的结果。
ens33
ens36
virbr0
virbr0-nic

1.先看开头:读取用户输入的网卡名
read -p "请输入要检查的网卡名(如 ens33/eth0):" nic
2.核心判断 1:网卡是否存在?
if ip link show "$nic" > /dev/null 2>&1; then
... 存在的逻辑...
else
echo -e "\n❌ 错误:网卡 $nic 不存在!"
fi
- 核心判断 2:网卡是否已启动(UP)?
if ip addr show "✅网卡nic 状态:已启动(UP)"
else
echo -e "\n❌ 网卡 $nic 状态:已关闭(DOWN)"
fi
步骤4:测试



PS:
- ens33/ens36(物理 / 虚拟机虚拟网卡)
通常是 UP 状态
这是你的主力网卡,负责和外部网络通信(比如连路由器、上互联网、和其他电脑传数据)。
系统启动时会自动把它设为 UP,除非你手动关闭(比如 sudo ip link set ens33 down)。 - virbr0(KVM 桥接网卡)
可能是 UP,也可能是 DOWN
这个网卡是 KVM 虚拟机的 "虚拟交换机",作用是让多个虚拟机之间能互相通信,或者虚拟机和宿主机通信 - virbr0-nic(virbr0的辅助网卡)
几乎都是 DOWN 状态
它是 virbr0 的 "配套马甲",本身不直接参与数据转发,只是为 virbr0 提供底层支持。
系统设计时就把它设为默认 DOWN,哪怕 virbr0 是 UP,它也不用启动------ 就像交换机的 "内部接口",不用对外暴露
4.其余琐碎知识点
-- 多行注释的写法验证;
#单行注释(已会)
<<EOF
这是多行注释第一行
这是多行注释第二行
可以写任意内容,不会被执行
EOF
-- test命令和[ ]的等价性验证;

-- ifconfig/netstat/ss的基础执行;

--脚本绝对路径执行的验证

二、基础作业:查询指定网口的启用状态
- 需求:编写脚本 check_port_status.sh ,接收一个网口名称作为参数(如 eth0 、 wlan0 ),输出该网口是 UP 还是 DOWN 状态
- 要求:参数为空时给出提示;兼容 ip link 和 ifconfig 两种命令的输出结果
- 练习环境:虚拟机 + 开发板
脚本如下
#!/bin/bash
#脚本名称:check_port_status.sh
#功能:查询指定网口的启用状态(兼容ip link和ifconfig)
#用法:./check_port_status.sh <网口名> (如:./check_port_status.sh ens33)
#第一步:检查参数是否为空
if [ $# -ne 1 ]; then #传入参数不是ens33这种规范输入
echo "❌ 错误:请传入网口名称作为参数!"
echo "✅ 使用方法:$0 <网口名>"
echo "🔍 示例:0 ens33 或 0 eth0"
exit 1
fi
#定义变量:接收传入的网口名参数
nic_name=$1
#第二步:优先使用ip link命令(新版)获取状态
if command -v ip > /dev/null 2>&1; then #判断系统里是否有 ip 命令
if ip link show "KaTeX parse error: Expected 'EOF', got '&' at position 25: ... > /dev/null 2>&̲1; then# 检查网口是否...(ip link show "$nic_name" | grep -oP '(UP|DOWN)(?=,|>)')
echo " 网口 n i c n a m e 状态: nic_name 状态: nicname状态:nic_status"
else
echo " 错误:网口 $nic_name 不存在!"
exit 1
fi
#第三步:如果没有ip命令,使用ifconfig命令(老版兼容)
elif command -v ifconfig > /dev/null 2>&1; then
检查网口是否存在
if ifconfig "KaTeX parse error: Expected 'EOF', got '&' at position 25: ... > /dev/null 2>&̲1; then ...nic_name" | grep -q "UP"; then
echo "📌 网口 $nic_name 状态:UP"
else
echo "📌 网口 $nic_name 状态:DOWN"
fi
else
echo "❌ 错误:网口 $nic_name 不存在!"
exit 1
fi
#第四步:两种命令都没有(极端情况)
else
echo "❌ 错误:系统中未找到ip和ifconfig命令,无法查询网口状态!"
exit 1
fi
虚拟机端测试

开发板端测试

三、基础作业:列出所有活跃网口及 IP 地址
- 需求:编写脚本 list_active_ports.sh ,自动筛选出所有状态为 UP 的网口,输出格式为 网口名称: IP地址
- 要求:无活跃网口时提示"当前无可用网口";忽略 lo 回环接口
- 练习环境:虚拟机 + 开发板
先确定格式,脚本格式匹配部分用AI生成

要是从ip addr里抠出「非 lo、UP、有 IPv4」的网口名 + 纯 IP


c
#!/bin/bash
#脚本名称:list_active_ports.sh
#功能:列出所有活跃(UP)网口及IP地址
#用法:./list_active_ports.sh
#初始化临时变量存储活跃网口信息
active_ports=""
#优先使用ip addr
if command -v ip > /dev/null 2>&1; then
#精准匹配ip addr输出格式
active_ports=$(
ip addr | awk '
# 1. 匹配网口行(如2: ens33: ...),提取网口名并重置UP标记
/^[0-9]+: [a-zA-Z0-9]+:/ {
split($2, nic_arr, ":"); # 按冒号分割,提取ens33/lo
current_nic = nic_arr[1];
is_up = 0; # 初始化为非UP状态
}
# 2. 匹配网口行中的UP状态(仅标记当前网口为UP)
/^[0-9]+: .*<.*UP,.*>/ && current_nic != "lo" {
is_up = 1; # 仅非lo的UP网口标记为1
}
# 3. 匹配IPv4地址行,且当前网口是UP状态
/inet [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ && is_up == 1 {
split($2, ip_arr, "/"); # 按/分割,提取纯IP(去掉/24)
ip_addr = ip_arr[1];
print current_nic ":" ip_addr; # 按"网口:IP"格式输出
is_up = 0; # 重置标记,避免重复匹配
}'
)
#兼容ifconfig(备用,开发板环境可用,但这块板子ip addr啥的也行)
elif command -v ifconfig > /dev/null 2>&1; then
active_ports=$(
ifconfig | awk '
/^[a-zA-Z0-9]+:/ {
nic_name = substr($1, 1, length($1)-1);
nic_up = 0;
}
/UP,BROADCAST,RUNNING/ && nic_name != "lo" {
nic_up = 1;
}
/inet / && nic_up == 1 {
print nic_name ":" $2;
nic_up = 0;
}'
)
#极端情况:无网络命令
else
echo "❌ 错误:系统未找到ip/ifconfig命令,无法查询网口信息!"
exit 1
fi
#输出最终结果
if [ -z "$active_ports" ]; then
echo "⚠️ 当前无可用网口"
else
echo "✅ 活跃网口及IP地址:"
echo "$active_ports"
fi
虚拟机测试

开发板测试


这倒没错,本身就没在用。

接上网线重新测试

结果没网口
原因:
开发板默认不会自动获取 IPv4,必须手动执行udhcpc或配静态 IP;
IPv6 是系统自动生成的链路本地地址(fe80 开头),不算 "可用的 IPv4 网口",所以脚本之前没识别。
修改脚本的核心匹配逻辑,让它能识别到eth1的<UP,LOWER_UP>状态(开发板插线后的 UP 格式),同时兼容 IPv6(可选)或优先识别 IPv4 配置后的状态
c
#!/bin/bash
#脚本名称:list_active_ports.sh
#功能:列出所有活跃(UP)网口及IP地址(适配虚拟机+IMX6ULL开发板)
#用法:./list_active_ports.sh
#初始化临时变量存储活跃网口信息
active_ports=""
#优先使用ip addr(适配开发板的UP状态格式)
if command -v ip > /dev/null 2>&1; then
# 核心修改:适配开发板eth1的<UP,LOWER_UP>状态,放宽UP匹配规则
active_ports=$(
ip addr | awk '
# 1. 匹配网口行(如3: eth1: ...),提取网口名并重置UP标记
/^[0-9]+: [a-zA-Z0-9]+:/ {
split($2, nic_arr, ":"); # 按冒号分割,提取eth1/lo
current_nic = nic_arr[1];
is_up = 0; # 初始化为非UP状态
}
# 2. 匹配UP状态(兼容两种格式:虚拟机<UP,xxx> / 开发板<xxx,UP,xxx>)
/^[0-9]+: .*<.*UP.*>/ && current_nic != "lo" {
is_up = 1; # 只要包含UP且非lo,就标记为活跃
}
# 3. 匹配IPv4地址行,且当前网口是UP状态
/inet [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ && is_up == 1 {
split($2, ip_arr, "/"); # 按/分割,提取纯IP(去掉/24)
ip_addr = ip_arr[1];
print current_nic ":" ip_addr; # 按"网口:IP"格式输出
is_up = 0; # 重置标记,避免重复匹配
}
# 可选:如果想显示IPv6(开发板eth1默认有IPv6),取消下面注释
# /inet6 fe80:/ && is_up == 1 {
# split($2, ip6_arr, "/");
# ip6_addr = ip6_arr[1];
# print current_nic ":IPv6-" ip6_addr;
# is_up = 0;
# }'
)
#兼容ifconfig(备用,开发板环境可用)
elif command -v ifconfig > /dev/null 2>&1; then
active_ports=$(
ifconfig | awk '
/^[a-zA-Z0-9]+:/ {
nic_name = substr($1, 1, length($1)-1);
nic_up = 0;
}
/UP,BROADCAST/ && nic_name != "lo" {
nic_up = 1;
}
/inet / && nic_up == 1 {
print nic_name ":" $2;
nic_up = 0;
}'
)
#极端情况:无网络命令
else
echo "❌ 错误:系统未找到ip/ifconfig命令,无法查询网口信息!"
exit 1
fi
#输出最终结果
if [ -z "$active_ports" ]; then
# 补充:提示用户网口UP但无IP的情况(开发板插线后常见)
up_nics=$(ip addr | awk '/^[0-9]+: .*<.*UP.*>/ && $2 !~ /lo:/ {split($2,n,":"); print n[1]}')
if [ -n "$up_nics" ]; then
echo "⚠️ 以下网口状态为UP,但未配置IPv4地址:"
echo "$up_nics"
echo "💡 建议执行:udhcpc -i 网口名(如udhcpc -i eth1)获取IP"
else
echo "⚠️ 当前无可用网口"
fi
else
echo "✅ 活跃网口及IP地址:"
echo "$active_ports"
fi

说明脚本没问题,现在解决IPv4的问题
udhcpc 执行失败
虚拟机查得IP为192.168.190.133
则
#核心:IP前三位和虚拟机一致(192.168.190),最后一位换101(避开虚拟机的133) ifconfig eth1 192.168.190.101 netmask 255.255.255.0 up
直接输还权限不够

这样配置一下就好了

四、进阶作业:网口流量实时监控(简易版)
- 需求:编写脚本 monitor_port_traffic.sh ,指定网口后,每隔2秒输出一次该网口的接收(RX)、发送(TX)字节数
- 要求:支持按 Ctrl+C 终止脚本;输出结果做格式化排版,清晰易读
- 练习环境:虚拟机 + 开发板
脚本内容如下
c
#!/bin/bash
#脚本名称:monitor_port_traffic.sh
#功能:实时监控指定网口的RX/TX字节数(每隔2秒输出,支持Ctrl+C终止)
#用法:./monitor_port_traffic.sh <网口名> (如:./monitor_port_traffic.sh ens33/eth0)
#定义优雅退出函数(捕获Ctrl+C)
exit_gracefully() {
echo -e "\n\n✅ 监控已停止!"
exit 0
}
#捕获SIGINT信号(Ctrl+C),触发退出函数
trap exit_gracefully 2
#2就是SIGINT,SIGINT就是按下 Ctrl+C 时系统给脚本发的 "中断指令, 替代默认行为,转而执行你指定的 exit_gracefully 函数
**#第一步:参数校验**
if [ $# -ne 1 ]; then
echo "❌ 错误:请传入唯一的网口名参数!"
echo "✅ 使用方法:./monitor_port_traffic.sh <网口名>"
echo "🔍 示例:./monitor_port_traffic.sh ens33 或 ./monitor_port_traffic.sh eth0"
exit 1
fi
#提取传入的网口名
nic_name="$1"
**#第二步:检查网口是否存在**
if ! ip link show "$nic_name" > /dev/null 2>&1; then
**加粗样式** echo "❌ 错误:网口 $nic_name 不存在!"
echo "🔍 可用网口列表:"
ip link show | awk -F ': ' '/^[0-9]+: /{print " - " $2}' | sed 's/://'
exit 1
fi
#! ip link show "$nic_name"判断网口存在,> /dev/null 2>&1静默处理
**#第三步:输出监控表头(格式化)**
echo "======================================== "
echo "📊 实时监控网口:$nic_name"
echo "⏱ 刷新间隔:2秒(按Ctrl+C终止)"
echo "======================================== "
printf "%-10s %-15s %-15s\n" "时间" "接收字节(RX)" "发送字节(TX)"
echo "----------------------------------------"
#第四步:实时循环采集流量
while true; do
# 获取当前时间(格式:HH:MM:SS)
current_time=$(date +"%H:%M:%S")
# 优先用ip -s link
if command -v ip > /dev/null 2>&1; then
# 提取RX/TX字节数(过滤指定网口,只取第一行字节数)
rx_bytes=$(ip -s link show "$nic_name" | awk '/RX:/{getline; print $1; exit}')
tx_bytes=$(ip -s link show "$nic_name" | awk '/TX:/{getline; print $1; exit}')
# 兼容ifconfig(开发板标配)
elif command -v ifconfig > /dev/null 2>&1; then
# 提取RX/TX字节数(ifconfig输出的RX/TX bytes)
rx_bytes=$(ifconfig "$nic_name" | awk '/RX bytes/{split($2, b, ":"); print b[2]}')
tx_bytes=$(ifconfig "$nic_name" | awk '/TX bytes/{split($2, b, ":"); print b[2]}')
# 极端情况:无网络命令
else
echo "❌ 错误:系统未找到ip/ifconfig命令,无法采集流量!"
exit 1
fi
# 格式化输出(对齐字段,确保美观)
printf "%-10s %-15s %-15s\n" "$current_time" "$rx_bytes Bytes" "$tx_bytes Bytes"
# 每隔2秒刷新一次
sleep 2
done
虚拟机测试

开发板测试

五、进阶作业:网口状态异常告警
- 需求:编写脚本 port_alert.sh ,定时检查指定网口状态,若状态变为 DOWN 或 IP 地址丢失,立即输出告警信息
- 要求:可设置检查间隔时间(如每10秒检查一次);在开发板环境中,可结合板载蜂鸣器或 LED 实现声光告警(若硬件支持)
- 练习环境:虚拟机(日志告警) + 开发板(日志+硬件告警)
这是给虚拟机的,开发板版本的在图片下面
c
#!/bin/bash
#网口状态异常告警脚本(纯日志版)
#使用说明:./port_alert.sh <网口名> <检查间隔(秒)>
#示例:./port_alert.sh eth1 10
#======================== 配置项 ========================
ALERT_LOG="/home/book/shell_demo/port_alert.log"
======================== 参数校验 ========================
#检查参数数量是否为2个
if [ $# -ne 2 ]; then
echo -e "\033[31m用法错误!\033[0m 正确用法:$0 <网口名> <检查间隔(秒)>"
echo "示例:$0 eth1 10 (检查eth1,每10秒一次)"
exit 1
fi
PORT_NAME=$1 # 要检查的网口(如eth1、ens33)
CHECK_INTERVAL=$2 # 检查间隔(秒)
#校验检查间隔是否为正整数
if ! [[ "$CHECK_INTERVAL" =~ ^[1-9][0-9]*$ ]]; then
echo -e "\033[31m错误:\033[0m 检查间隔必须是大于0的数字(如5、10)!"
exit 1
fi
#======================== 核心函数 ========================
#1. 检查网口是否存在
check_port_exist() {
if ! ip link show "$PORT_NAME" >/dev/null 2>&1; then
echo -e "\033[31m[$(date +'%Y-%m-%d %H:%M:%S')] 错误:\033[0m 网口$PORT_NAME不存在!"
exit 1
fi
}
#2. 检查网口状态(UP/DOWN)+ IP地址是否存在
check_port_status() {
# 获取网口状态(state后的字段,正常为UP)
PORT_STATUS=$(ip link show "$PORT_NAME" | grep -Eo 'state [A-Z]+' | awk '{print $2}')
# 获取网口IPv4地址(空则表示IP丢失)
PORT_IP=$(ip addr show "$PORT_NAME" | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)
# 初始化告警信息
ALERT_MSG=""
# 判定异常场景
if [ "$PORT_STATUS" != "UP" ]; then
ALERT_MSG="网口$PORT_NAME状态异常:当前为$PORT_STATUS(正常应为UP)"
elif [ -z "$PORT_IP" ]; then
ALERT_MSG="网口$PORT_NAME IP地址丢失!"
fi
# 异常:输出告警(终端+日志)
if [ -n "$ALERT_MSG" ]; then
local log_msg="[$(date +'%Y-%m-%d %H:%M:%S')] 告警:$ALERT_MSG"
# 终端红色高亮输出
echo -e "\033[31m$log_msg\033[0m"
# 写入日志文件(追加模式)
echo "$log_msg" >> "$ALERT_LOG"
# 正常:输出普通日志
else
local normal_msg="[$(date +'%Y-%m-%d %H:%M:%S')] 网口$PORT_NAME状态正常(UP),IP:$PORT_IP"
echo "$normal_msg"
# 正常日志也可写入文件(可选,取消下面注释即可)
# echo "$normal_msg" >> "$ALERT_LOG"
fi
}
#======================== 主程序 ========================
# 前置检查:网口是否存在
check_port_exist
#初始化日志文件(确保可写)
touch "$ALERT_LOG" >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo -e "\033[33m警告:\033[0m 日志文件$ALERT_LOG创建失败,仅输出终端告警!"
ALERT_LOG="/tmp/port_alert.log" # 降级到临时目录
touch "$ALERT_LOG"
fi
#启动提示
echo -e "\033[32m[$(date +'%Y-%m-%d %H:%M:%S')] 启动网口告警脚本:\033[0m"
echo " - 检查网口:$PORT_NAME"
echo " - 检查间隔:$CHECK_INTERVAL秒"
echo " - 告警日志:$ALERT_LOG"
echo "----------------------------------------"
#循环检查(Ctrl+C停止)
while true; do
check_port_status
sleep "$CHECK_INTERVAL"
done
虚拟机测试

开发板测试(更改脚本)



从韦东山的代码里找对应引脚

让AI更改适配
有led_ctrl.sh和port_alert.sh
更改之后的版本如下
led_ctrl.sh:
c
#!/bin/bash
# IMX6ULL LED控制脚本(适配BusyBox版devmem)
# 用法:/tmp/led_ctrl.sh on/off
# 寄存器物理地址(和你C代码完全一致)
CCM_CCGR1=0x20C406C
IOMUXC_SNVS_MUX=0x2290014
GPIO5_GDIR=0x020AC004
GPIO5_DR=0x020AC000
# 初始化LED(适配BusyBox devmem语法)
led_init() {
# 1. 读取CCM_CCGR1当前值
CCM_VAL=$(devmem $CCM_CCGR1 32)
# 使能GPIO5时钟(bit31:30 = 0b11)
NEW_CCM_VAL=$((CCM_VAL | (3<<30)))
devmem $CCM_CCGR1 32 $NEW_CCM_VAL
# 2. 读取IOMUXC_SNVS_MUX当前值
IOMUX_VAL=$(devmem $IOMUXC_SNVS_MUX 32)
# 配置引脚为GPIO功能(bit3:0 = 0b0101)
NEW_IOMUX_VAL=$(((IOMUX_VAL & ~0xf) | 5))
devmem $IOMUXC_SNVS_MUX 32 $NEW_IOMUX_VAL
# 3. 读取GPIO5_GDIR当前值
GDIR_VAL=$(devmem $GPIO5_GDIR 32)
# 设置GPIO5_IO03为输出(bit3 = 1)
NEW_GDIR_VAL=$((GDIR_VAL | (1<<3)))
devmem $GPIO5_GDIR 32 $NEW_GDIR_VAL
}
# 控制LED
case "$1" in
on)
led_init # 初始化(首次执行生效)
# 读取GPIO5_DR当前值,低电平点亮(bit3清0)
DR_VAL=$(devmem $GPIO5_DR 32)
NEW_DR_VAL=$((DR_VAL & ~(1<<3)))
devmem $GPIO5_DR 32 $NEW_DR_VAL
;;
off)
led_init
# 读取GPIO5_DR当前值,高电平熄灭(bit3置1)
DR_VAL=$(devmem $GPIO5_DR 32)
NEW_DR_VAL=$((DR_VAL | (1<<3)))
devmem $GPIO5_DR 32 $NEW_DR_VAL
;;
*)
echo "用法:$0 <on|off>"
exit 1
;;
esac
exit 0
port_alert.sh:
#!/bin/bash
# IMX6ULL开发板网口状态告警脚本(日志+LED告警)
# 功能:定时检查网口状态,DOWN/IP丢失时日志告警+点亮LED,恢复后熄灭LED
# 使用说明:./port_alert.sh <网口名> <检查间隔(秒)>
# 示例:./port_alert.sh eth0 10 (检查eth1,每5秒一次)
ALERT_LOG="/tmp/port_alert.log" # 日志路径(/tmp无需权限,重启丢失;想持久化改到/root)
LED_CTRL_TOOL="/tmp/led_ctrl.sh" # LED控制脚本路径(必须和你的实际路径一致)
LED_ENABLE=1 # LED告警开关:1=启用,0=禁用
# ======================== 参数校验(必做) ========================
# 1. 检查参数数量
if [ $# -ne 2 ]; then
echo -e "\033[31m[ERROR] 用法错误!正确用法:$0 <网口名> <检查间隔(秒)>\033[0m"
echo -e "\033[33m[INFO] 示例:$0 eth0 10\033[0m"
exit 1
fi
# 2. 提取参数
PORT_NAME=$1 # 要检查的网口(IMX6ULL通常是eth0)
CHECK_INTERVAL=$2 # 检查间隔(秒)
# 3. 校验检查间隔是否为正整数
if ! [[ "$CHECK_INTERVAL" =~ ^[1-9][0-9]*$ ]]; then
echo -e "\033[31m[ERROR] 检查间隔必须是大于0的整数!\033[0m"
exit 1
fi
# 4. 校验网口是否存在
if ! ip link show "$PORT_NAME" >/dev/null 2>&1; then
echo -e "\033[31m[ERROR] 网口$PORT_NAME不存在!\033[0m"
exit 1
fi
# 5. 校验LED脚本是否存在(不存在则禁用LED告警)
if [ $LED_ENABLE -eq 1 ] && [ ! -f "$LED_CTRL_TOOL" ]; then
echo -e "\033[33m[WARN] LED控制脚本$LED_CTRL_TOOL不存在,禁用LED告警!\033[0m"
LED_ENABLE=0
fi
# ======================== 核心函数 ========================
# 1. LED告警控制(点亮/熄灭)
led_alert() {
local status=$1 # on=点亮,off=熄灭
if [ $LED_ENABLE -eq 1 ]; then
$LED_CTRL_TOOL $status >/dev/null 2>&1
fi
}
# 2. 网口状态检查+告警逻辑
check_port_status() {
# 获取网口状态(UP/DOWN)
PORT_STATUS=$(ip link show "$PORT_NAME" | grep -Eo 'state [A-Z]+' | awk '{print $2}')
# 获取网口IPv4地址(仅保留IP,去掉掩码)
PORT_IP=$(ip addr show "$PORT_NAME" | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)
# 场景1:网口DOWN → 告警+点亮LED
if [ "$PORT_STATUS" != "UP" ]; then
LOG_MSG="[$(date +'%Y-%m-%d %H:%M:%S')] [ALERT] 网口$PORT_NAME状态异常:当前为$PORT_STATUS(正常应为UP)"
echo -e "\033[31m$LOG_MSG\033[0m" # 终端红色告警
echo "$LOG_MSG" >> "$ALERT_LOG" # 写入日志
led_alert "on" # 点亮LED
# 场景2:网口UP但IP丢失 → 告警+点亮LED
elif [ -z "$PORT_IP" ]; then
LOG_MSG="[$(date +'%Y-%m-%d %H:%M:%S')] [ALERT] 网口$PORT_NAME状态为UP,但IP地址丢失!"
echo -e "\033[31m$LOG_MSG\033[0m"
echo "$LOG_MSG" >> "$ALERT_LOG"
led_alert "on"
# 场景3:网口正常 → 提示+熄灭LED
else
LOG_MSG="[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] 网口$PORT_NAME状态正常(UP),IP地址:$PORT_IP"
echo -e "\033[32m$LOG_MSG\033[0m" # 终端绿色提示
# 正常日志可选写入(取消注释启用)
# echo "$LOG_MSG" >> "$ALERT_LOG"
led_alert "off" # 熄灭LED
fi
}
# ======================== 主程序(定时循环检查) ========================
# 启动提示
echo -e "\033[32m[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] IMX6ULL网口告警脚本启动!\033[0m"
echo -e "\033[32m[INFO] 检查网口:$PORT_NAME | 检查间隔:$CHECK_INTERVAL秒 | LED告警:$(if [ $LED_ENABLE -eq 1 ]; then echo "启用"; else echo "禁用"; fi)\033[0m"
echo -e "\033[32m[INFO] 按Ctrl+C停止脚本...\033[0m"
echo "--------------------------------------------------------"
# 无限循环检查(定时)
while true; do
check_port_status
sleep "$CHECK_INTERVAL"
done
六、综合作业:网口信息汇总报告生成
- 需求:编写脚本 port_info_report.sh ,一次性收集所有网口的名称、状态、IP 地址、MAC 地址、接收/发送字节数,并将结果写入 port_report_$(date +%F).txt 报告文件
- 要求:报告文件按日期命名,避免覆盖;内容按网口分类整理,添加标题和生成时间
- 练习环境:虚拟机 + 开发板
c
#!/bin/bash
# 网口信息汇总报告脚本(适配虚拟机+IMX6ULL开发板)
# 功能:采集所有网口信息,生成按日期命名的结构化报告
# 使用:./port_info_report.sh(无需参数,直接运行)
# ======================== 基础配置 ========================
# 报告文件命名:port_report_YYYY-MM-DD.txt(按日期命名,避免覆盖)
REPORT_FILE="port_report_$(date +%F).txt"
# 报告生成时间(精确到秒)
REPORT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
# ======================== 核心函数:采集单网口信息 ========================
get_port_info() {
local port_name=$1 # 传入网口名(如eth0/ens33/lo)
# 1. 优化核心:精准识别网口状态(UP/DOWN/未激活)
if ip link show "$port_name" | grep -q 'UP'; then
port_status="UP"
elif ip link show "$port_name" | grep -q 'DOWN'; then
port_status="DOWN"
else
port_status="未激活" # 无UP/DOWN标记时标注,替代原UNKNOWN
fi
# 2. 获取IPv4地址(无则标注"无",仅取第一个IP)
port_ip=$(ip addr show "$port_name" | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | head -1)
[ -z "$port_ip" ] && port_ip="无"
# 3. 获取MAC地址(lo无MAC则标注"无")
port_mac=$(ip link show "$port_name" | grep -Eo 'link/ether [0-9a-f:]+' | awk '{print $2}')
[ -z "$port_mac" ] && port_mac="无"
# 4. 获取接收/发送字节数(从sysfs读取,适配嵌入式系统)
port_rx_bytes=$(cat "/sys/class/net/$port_name/statistics/rx_bytes" 2>/dev/null || echo "0")
port_tx_bytes=$(cat "/sys/class/net/$port_name/statistics/tx_bytes" 2>/dev/null || echo "0")
# 5. 将该网口信息写入报告(结构化排版)
echo "------------------------------" >> "$REPORT_FILE"
echo "网口名称:$port_name" >> "$REPORT_FILE"
echo "状态:$port_status" >> "$REPORT_FILE"
echo "IP地址:$port_ip" >> "$REPORT_FILE"
echo "MAC地址:$port_mac" >> "$REPORT_FILE"
echo "接收字节数:$port_rx_bytes" >> "$REPORT_FILE"
echo "发送字节数:$port_tx_bytes" >> "$REPORT_FILE"
}
# ======================== 主程序 ========================
# 1. 清空/创建报告文件(按日期命名,无需担心覆盖)
> "$REPORT_FILE"
# 2. 写入报告标题和生成时间(提升可读性)
echo "========== 网口信息汇总报告 ==========" >> "$REPORT_FILE"
echo "报告生成时间:$REPORT_TIME" >> "$REPORT_FILE"
echo "======================================" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# 3. 获取所有有效网口列表(过滤无用虚拟网口,保留物理/回环口)
port_list=$(ip link show | grep -E '^[0-9]+: ' | awk -F': ' '{print $2}' | grep -v '^br-' | grep -v '^veth' | grep -v '^docker')
# 4. 遍历所有网口,逐个采集信息
for port in $port_list; do
get_port_info "$port"
done
# 5. 补充报告结尾,完善结构
echo "" >> "$REPORT_FILE"
echo "========== 报告结束 ==========" >> "$REPORT_FILE"
# 6. 终端友好提示(告知用户报告生成结果)
echo -e "\033[32m[SUCCESS] 网口报告已生成:$(pwd)/$REPORT_FILE\033[0m"
开发板测试

开发板测试
Device "sit0@NONE" does not exist. 是脚本遍历网口时,系统中存在一个无效的 / 未正确初始化的sit0网口条目,导致ip link show命令报的警告,本质是系统层面的小问题,不是脚本错误。介意的话可以用grep过滤掉
#优化:过滤无效网口(sit0)和特殊字符(@NONE),仅保留有效网口
port_list=(ip link show \| grep -E '^[1](#1)^+: ' \| awk -F': ' '{print 2}' |
grep -v '^br-' | grep -v '^veth' | grep -v '^docker' |
grep -v '^sit' | grep -v '@' | tr -d ' ') # 新增:过滤sit开头+@字符+空格


- 0-9 ↩︎