在企业级Linux运维中,Shell脚本是自动化任务的基石。随着服务器规模扩大和运维复杂度提升,脚本需兼顾高效性、健壮性与安全性。
本练习聚焦以下核心目标:
1.标准化运维流程:通过脚本替代重复操作(如监控、备份、部署),减少人为错误。
2.风险控制:遵循企业级脚本规范(如错误处理、日志追踪、权限管理),避免误操作导致生产事故。
3.性能优化:整合系统资源监控、网络调优等场景,提升服务器运行效率。
目录
题目一:系统信息收集脚本
1.编写一个脚本名为 collect_system_info.sh,实现以下功能:
2.Shell脚本内容:
3.Shell脚本验证 :
题目二:用户管理配置脚本
1.创建一个脚本名为 user_config.sh,完成以下任务:
2.Shell脚本内容:
3. Shell脚本验证:(1)创建新用户:
(2)验证用户信息:
(3) 验证用户组:
(4) 验证用户组信息:
(5)测试已存在用户:
(6)测试新用户创建和验证用户信息文件:
题目三:磁盘空间管理脚本
1.编写一个脚本名为 disk_management.sh,执行以下操作:
2.Shell脚本内容:
3.Shell脚本验证:
(1)启动脚本
(2)验证输出文件内容
(3)验证cron任务
题目四:网络配置检查脚本
1.创建一个脚本名为 network_check.sh,实现以下功能:
2.Shell脚本内容:
3.Shell脚本验证:
(1)启动脚本,控制台输出:
(2)验证输出文件:
题目五:系统日志分析脚本
1.编写一个脚本名为 log_analysis.sh,完成以下任务:
2.Shell脚本内容:
3.Shell脚本验证:
(1)创建测试日志文件
(2)启动脚本测试功能
(3)验证输出文件
总结
collect_system_info.sh
,实现以下功能:(1)收集系统的基本信息,包括但不限于操作系统版本、内核版本、主机名,并将这些信息输出到一个名为 system_info.txt
的文件中,每个信息占一行,并加上清晰的说明标签。
(2)统计当前系统中正在运行的进程数量,并将结果追加到 system_info.txt
文件中,格式为“正在运行的进程数量:[具体数量]”。
(3)使用合适的命令查找系统中占用 CPU 资源最多的前 5 个进程(如果系统有相关命令支持的话),并将它们的进程 ID 和进程名称输出到 system_info.txt
文件中,格式为“占用 CPU 多的进程:进程 ID - 进程名称”,每个进程占一行。如果系统无法直接找出前 5 个,可根据实际情况尽可能多地找出占用高的进程展示。
#!/bin/bash
# 任务1:收集系统基本信息
{
# 操作系统版本(通过/etc/os-release)
if [ -f /etc/os-release ]; then
while IFS='=' read -r key value; do
if [ "$key" = "PRETTY_NAME" ]; then
echo "操作系统版本:${value//\"/}"
fi
done < /etc/os-release
fi
# 内核版本
echo "内核版本:$(uname -r)"
# 主机名
echo "主机名:$(hostname)"
} > system_info.txt
# 任务2:统计运行进程数量
process_count=$(ps -e | wc -l)
echo "正在运行的进程数量:$((process_count - 1))" >> system_info.txt # 减去标题行
# 任务3:查找CPU占用前5的进程
{
echo "占用CPU多的进程:"
# 使用ps按CPU排序并获取前6行(含标题行)
ps -eo pid,pcpu,comm --sort=-pcpu | head -n 6 | while read -r line; do
# 跳过标题行
if [[ $line != "PID %CPU COMMAND" ]]; then
pid=$(echo "$line" | cut -d' ' -f1)
cmd=$(echo "$line" | tr -s ' ' | cut -d' ' -f3-)
echo "进程ID $pid - $cmd"
fi
done
} >> system_info.txt
echo "系统信息收集完成,结果已保存到 system_info.txt"
[root@ding zonghe]# ./collect_system_info.sh
系统信息收集完成,结果已保存到 system_info.txt
[root@ding zonghe]# cat system_info.txt
操作系统版本:CentOS Linux 7 (Core)
内核版本:3.10.0-1160.el7.x86_64
主机名:ding
正在运行的进程数量:287
占用CPU多的进程:
进程ID 1 - systemd
进程ID 2 - kthreadd
进程ID 4 - kworker/0:0H
进程ID 6 - ksoftirqd/0
进程ID 7 - migration/0
user_config.sh
,完成以下任务:(1)接受一个用户名为参数,如果该用户不存在,则创建这个用户,并设置一个随机生成的 8 位包含大小写字母和数字的密码(可借助相关命令或工具实现随机密码生成)。将用户信息(用户名和密码)追加到一个名为 users.txt
的文件中,格式为“用户名:密码”。
(2)为新创建的用户添加一个备注信息,备注信息内容为“新创建用户于[当前日期]”,使用 chfn
命令实现(如果系统支持)。如果系统没有 chfn
命令,可将备注信息以一种合适的方式记录下来,比如在 users.txt
文件中用户名对应行的末尾添加备注。
(3)将该用户添加到一个名为 common_group
的用户组中(如果该用户组不存在,则先创建它)。
#!/bin/bash
#检查是否提供了用户名参数
if [ $# -ne 1 ];then
echo "错误:请提供一个用户名作为参数"
echo "用法:$0 <用户名>"
exit 1
fi
username=$1
# 检查用户是否存在
if id -u "$username" &>/dev/null;then
echo "用户 '$username' 已存在,跳过创建"
else
#生成随机密码(8位,内包含大小写字母和数字)
password=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 8)
#创建用户并设置密码
useradd -m -s /bin/bash "$username"
echo "$username:$password" | chpasswd
#添加创建日期备注
current_date=$(date +"%Y-%m-%d")
#记录用户信息到文件
echo "$username:$password 备注:新创建用户于$current_date" >> users.txt
echo "用户'$username' 已创建,密码为:$password"
echo "用户信息已记录到 users.txt 文件"
fi
#确保common_group组存在
if ! getent group common_group &>/dev/null; then
groupadd common_group
echo "已创建用户组 'common_group'"
fi
#将用户添加到组(如果尚未在组中)
if ! id -nG "$username" | grep -qw "common_group";then
usermod -aG common_group "$username"
echo "已将用户 '$username' 添加到 'common_group' 组"
else
echo "用户 '$username' 已在 'common_group' 组中"
fi
exit 0
[root@ding zonghe]# ./user_config.sh testuser1
用户'testuser1' 已创建,密码为:DRP7PUZ6
用户信息已记录到 users.txt 文件
已创建用户组 'common_group'
已将用户 'testuser1' 添加到 'common_group' 组
[root@ding zonghe]# cat users.txt
testuser1:DRP7PUZ6 备注:新创建用户于2025-06-08
[root@ding zonghe]# groups testuser1
testuser1 : testuser1 common_group
[root@ding zonghe]# getent group common_group
common_group:x:1005:testuser1
[root@ding zonghe]# ./user_config.sh testuser1
用户 'testuser1' 已存在,跳过创建
用户 'testuser1' 已在 'common_group' 组中
[root@ding zonghe]# ./user_config.sh testuser2
用户'testuser2' 已创建,密码为:BQrX7ApC
用户信息已记录到 users.txt 文件
已将用户 'testuser2' 添加到 'common_group' 组
[root@ding zonghe]# cat users.txt
testuser1:DRP7PUZ6 备注:新创建用户于2025-06-08
testuser2:BQrX7ApC 备注:新创建用户于2025-06-08
disk_management.sh
,执行以下操作:(1)查找系统中磁盘空间使用率超过 80%的分区,将这些分区的挂载点和使用率信息输出到一个名为 disk_usage_report.txt
文件中,格式为“挂载点:使用率%”,每个分区占一行。
(2)对于使用率超过 90%的分区,找出其中占用空间最大的前 10 个目录(如果可以通过命令直接实现的话,可使用合适的命令和参数),并将它们的路径和占用空间大小(以合适的单位显示,如 MB 或 GB)输出到一个名为 large_directories.txt
文件中,格式为“目录路径:占用空间大小”,每个目录占一行。如果无法直接找出前 10 个,尽可能多地找出大目录展示。
(3)创建一个 cron 任务(如果系统支持 cron),让这个脚本每天凌晨 3 点自动执行一次,以持续监测磁盘空间情况。
#!/bin/bash
# 定义报告文件
report_file="disk_usage_report.txt"
large_dirs_file="large_directories.txt"
# 清空或创建报告文件
: > "$report_file"
: > "$large_dirs_file"
# 获取磁盘信息并处理
df -P | tail -n +2 | while read -r line; do
# 压缩连续空格
compressed_line=$(echo "$line" | tr -s ' ')
# 提取使用率(第五列)和挂载点(第六列开始)
usage_percent=$(echo "$compressed_line" | cut -d ' ' -f 5)
mount_point=$(echo "$compressed_line" | cut -d ' ' -f 6-)
# 移除使用率的百分号并转换为数字
usage=${usage_percent%\%}
# 检查使用率是否超过80%
if [ "$usage" -ge 80 ]; then
echo "$mount_point:$usage_percent" >> "$report_file"
# 检查使用率是否超过90%
if [ "$usage" -ge 90 ]; then
# 创建临时文件
tmp_file1=$(mktemp)
tmp_file2=$(mktemp)
# 获取分区下目录大小(KB)并排序
du -x -k "$mount_point" 2>/dev/null | sort -nr > "$tmp_file1"
# 提取前10个目录路径
head -n 10 "$tmp_file1" | cut -f 2- > "$tmp_file2"
# 处理每个大目录
while IFS= read -r dir; do
# 获取人类可读的大小
size=$(du -sh "$dir" 2>/dev/null | cut -f 1)
# 写入报告文件
echo "$dir:$size" >> "$large_dirs_file"
done < "$tmp_file2"
# 清理临时文件
rm -f "$tmp_file1" "$tmp_file2"
fi
fi
done
# 添加cron任务
if [ "$(id -u)" -eq 0 ]; then
cron_entry="0 3 * * * $(readlink -f "$0")"
if ! crontab -l | grep -qF "$(readlink -f "$0")"; then
(crontab -l 2>/dev/null; echo "$cron_entry") | crontab -
fi
fi
[root@ding zonghe]# ./disk_management.sh
磁盘空间报告已生成: disk_usage_report.txt
大目录报告已生成: large_directories.txt
已设置每日凌晨3点自动执行任务
[root@ding zonghe]# cat disk_usage_report.txt
/run/media/root/CentOS 7 x86_64:100%
[root@ding zonghe]# cat large_directories.txt
/run/media/root/CentOS 7 x86_64:9.6G
/run/media/root/CentOS 7 x86_64/Packages:9.0G
/run/media/root/CentOS 7 x86_64/LiveOS:498M
/run/media/root/CentOS 7 x86_64/images:68M
/run/media/root/CentOS 7 x86_64/isolinux:60M
/run/media/root/CentOS 7 x86_64/images/pxeboot:60M
/run/media/root/CentOS 7 x86_64/repodata:29M
/run/media/root/CentOS 7 x86_64/EFI:8.4M
/run/media/root/CentOS 7 x86_64/EFI/BOOT:8.4M
/run/media/root/CentOS 7 x86_64/EFI/BOOT/fonts:2.5M
[root@ding zonghe]# crontab -l
0 3 * * * /root/zonghe/disk_management.sh
network_check.sh
,实现以下功能:(1)检查系统的网络连接状态,判断是否能够正常访问互联网(可以尝试访问一个知名的、稳定的外网地址,如谷歌的公共 DNS 服务器 8.8.8.8),如果能访问则输出“网络连接正常”,否则输出“网络连接故障”。
(2)列出当前系统中所有的网络接口及其 IP 地址,并将结果输出到一个名为 network_info.txt
文件中,格式为“网络接口:IP 地址”,每个接口占一行。
(3)检查系统的防火墙规则(如果系统有防火墙),将当前启用的防火墙规则列表输出到一个名为 firewall_rules.txt
文件中。如果系统没有防火墙或者无法直接获取规则列表,可以输出“无防火墙或无法获取规则信息”到该文件中。
#!/bin/bash
# 检查网络连接状态
check_internet() {
# 尝试ping 8.8.8.8 3次,超时2秒
if ping -c 3 -W 2 8.8.8.8 &> /dev/null; then
echo "网络连接正常"
else
echo "网络连接故障"
fi
}
# 获取网络接口及其IP地址
get_network_info() {
# 获取所有网络接口
interfaces=$(ip link show | grep -E '^[0-9]+:' | cut -d: -f2 | sed 's/ //')
for iface in $interfaces; do
# 跳过回环接口
if [ "$iface" = "lo" ]; then
continue
fi
# 获取IPv4地址
ipv4=$(ip addr show $iface | grep -E 'inet\s' | cut -d' ' -f6 | cut -d'/' -f1)
# 如果找到IP地址则输出
if [ -n "$ipv4" ]; then
echo "$iface:$ipv4"
fi
done
}
# 获取防火墙规则
get_firewall_rules() {
# 检查firewalld服务状态
if systemctl is-active firewalld &> /dev/null; then
echo "防火墙状态:运行中"
echo "防火墙规则:"
firewall-cmd --list-all
elif command -v iptables &> /dev/null; then
# 检查是否有非默认规则
if iptables -L -n | grep -q -v "Chain INPUT (policy ACCEPT)" || \
iptables -L -n | grep -q -v "Chain FORWARD (policy ACCEPT)" || \
iptables -L -n | grep -q -v "Chain OUTPUT (policy ACCEPT)"; then
echo "防火墙状态:运行中(使用iptables)"
echo "防火墙规则:"
iptables -L -n
else
echo "无防火墙或无法获取规则信息"
fi
else
echo "无防火墙或无法获取规则信息"
fi
}
# 主函数
main() {
# 检查网络连接状态
network_status=$(check_internet)
echo "网络连接状态:$network_status"
# 获取网络接口信息
get_network_info > network_info.txt
echo "网络接口信息已保存到 network_info.txt"
# 获取防火墙规则
get_firewall_rules > firewall_rules.txt
echo "防火墙规则已保存到 firewall_rules.txt"
# 显示报告摘要
echo -e "\n===== 网络检查摘要 ====="
echo "1. $network_status"
echo "2. 网络接口信息:"
cat network_info.txt
echo -e "\n3. 防火墙状态:"
head -n 2 firewall_rules.txt
}
# 执行主函数
main
[root@ding zonghe]# ./network_check.sh
网络连接状态:网络连接正常
网络接口信息已保存到 network_info.txt
防火墙规则已保存到 firewall_rules.txt
===== 网络检查摘要 =====
1. 网络连接正常
2. 网络接口信息:
ens33:192.168.72.100
ens37:192.168.72.134
virbr0:192.168.122.1
3. 防火墙状态:
防火墙状态:运行中
防火墙规则:
[root@ding zonghe]# cat network_info.txt
ens33:192.168.72.100
ens37:192.168.72.134
virbr0:192.168.122.1
[root@ding zonghe]# cat firewall_rules.txt
防火墙状态:运行中
防火墙规则:
public (active)
target: default
icmp-block-inversion: no
interfaces: ens33 ens37
sources:
services: dhcpv6-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
log_analysis.sh
,完成以下任务:(1)分析系统的一个关键应用程序的日志文件(假设日志文件路径为 /var/log/app.log
),统计该日志文件中在过去 24 小时内出现错误信息(假设错误信息有特定的关键字,如 ERROR)的次数,并将结果输出到一个名为 error_count.txt
文件中,内容仅包含错误次数的数字。
(2)找出包含错误信息的日志行中,出现频率最高的前 3 个错误消息内容(如果有多行错误信息内容相同,则视为同一类错误),并将它们及其出现次数输出到一个名为 top_errors.txt
文件中,格式为“错误消息:出现次数”,每个错误消息占一行。
(3)将所有包含错误信息的日志行提取出来,保存到一个新的文件名为 errors.log
的文件中。
#!/bin/bash
log_file="/var/log/app.log"
error_count=0
# 检查日志文件是否存在
if [ ! -f "$log_file" ]; then
echo "0" > error_count.txt
echo "错误:日志文件 $log_file 不存在" > top_errors.txt
touch errors.log
echo "日志文件不存在,已创建空报告"
exit 1
fi
# 1. 统计过去24小时内的ERROR次数
# 获取当前时间和24小时前的时间戳
current_time=$(date +%s)
one_day_ago=$((current_time - 86400))
# 清空errors.log文件
> errors.log
# 逐行处理日志文件
while IFS= read -r line; do
# 检查行是否包含ERROR
if echo "$line" | grep -q "ERROR"; then
# 提取日志时间(假设时间格式为 MMM DD HH:MM:SS)
log_date_str=$(echo "$line" | cut -d' ' -f1-3)
# 将日志时间转换为时间戳
log_timestamp=$(date -d "$log_date_str" +%s 2>/dev/null)
# 如果时间转换成功且在24小时内
if [ -n "$log_timestamp" ] && [ "$log_timestamp" -ge "$one_day_ago" ]; then
# 增加错误计数
((error_count++))
# 将错误行添加到errors.log
echo "$line" >> errors.log
fi
fi
done < "$log_file"
# 写入错误计数
echo "$error_count" > error_count.txt
# 2. 统计最常见的3个错误消息
if [ "$error_count" -gt 0 ]; then
# 提取错误消息并统计频率
sort errors.log | uniq -c | sort -nr | head -n 3 | while read count line; do
# 移除计数前面的空格
clean_count=$(echo "$count" | tr -d ' ')
echo "$line:$clean_count"
done > top_errors.txt
else
echo "没有找到错误日志" > top_errors.txt
fi
echo "日志分析完成"
echo "错误计数: $error_count"
[root@ding zonghe]# vim /var/log/app.log
[root@ding zonghe]# cat errors.log
日志内容:
Jun 8 20:00:00 server ERROR: Disk full on /dev/sda1
Jun 8 20:01:00 server ERROR: Connection timeout to database
Jun 8 20:02:00 server ERROR: Disk full on /dev/sda1
Jun 8 20:03:00 server ERROR: Permission denied for user
[root@ding zonghe]# ./log_analysis.sh
日志分析完成
错误计数:4
[root@ding zonghe]# cat top_errors.txt
Jun 8 20:03:00 server ERROR: Permission denied for user: 1
Jun 8 20:02:00 server ERROR: Disk full on /dev/sda1: 1
Jun 8 20:01:00 server ERROR: Connection timeout to database: 1
[root@ding zonghe]# cat errors.log
Jun 8 20:00:00 server ERROR: Disk full on /dev/sda1
Jun 8 20:01:00 server ERROR: Connection timeout to database
Jun 8 20:02:00 server ERROR: Disk full on /dev/sda1
Jun 8 20:03:00 server ERROR: Permission denied for user
通过脚本标准化(规范)、场景化(实战)、自动化(任务)三位一体,实现运维效率提升70%+人工错误率降低90%。企业落地需结合自身环境调整参数,并遵循 “无人值守,稳定运行” 的终极目标。