引言:为什么要学习Shell脚本?
在学习Shell脚本之前,我们先思考一个问题:你是否曾经因为重复性的系统管理工作而感到头疼?比如每天需要手动检查多个服务器的磁盘使用情况,或者需要批量处理大量的日志文件?如果是的话,那么Shell脚本就是你的救星。
Shell脚本是Linux系统管理员的”秘密武器”,它能够帮助你自动化完成各种系统管理任务,提高工作效率,减少人为错误。无论你是刚刚接触Linux的新手,还是有一定经验的老手,掌握Shell脚本都是一项非常有价值的技能。
Shell脚本基础入门
什么是Shell脚本?
Shell脚本是一个包含一系列命令的文本文件,这些命令会被Shell解释执行。Shell是Linux系统的命令行解释器,常见的有bash、sh、zsh等。
让我用一个简单的例子来演示如何创建你的第一个Shell脚本:
#!/bin/bash
# 这是你的第一个Shell脚本
echo "Hello, World!"
echo "当前日期: $(date)"
echo "当前用户: $(whoami)"
如何创建和运行Shell脚本?
- 创建脚本文件:
nano myscript.sh
编写脚本内容(如上例所示)
添加执行权限:
chmod +x myscript.sh
- 执行脚本:
./myscript.sh
注释和变量
在Shell脚本中,注释以#开头,用于解释脚本的功能。变量是存储数据的容器,使用方法如下:
#!/bin/bash
# 定义变量
name="张三"
age=25
city="北京"
# 使用变量
echo "姓名: $name"
echo "年龄: $age 岁"
echo "城市: $city"
# 变量计算
sum=$((age + 10))
echo "$name 10年后将 $sum 岁"
条件判断与循环结构
条件判断(if语句)
条件判断是Shell脚本中非常重要的组成部分,它允许脚本根据不同的条件执行不同的操作。
#!/bin/bash
# 温度检测脚本
temperature=35
if [ $temperature -gt 40 ]; then
echo "警告:温度过高,请注意散热!"
elif [ $temperature -gt 30 ]; then
echo "注意:温度较高,建议减少运行负荷"
elif [ $temperature -gt 20 ]; then
echo "温度适宜,系统运行正常"
else
echo "温度较低,注意保暖"
fi
循环结构
循环结构允许我们重复执行一系列命令,这在处理大量文件或执行批量操作时非常有用。
#!/bin/bash
# 批量创建用户示例
users=("zhangsan" "lisi" "wangwu" "zhaoliu")
for user in "${users[@]}"; do
echo "正在创建用户: $user"
useradd -m -s /bin/bash "$user"
echo "用户 $user 创建成功"
done
# 使用while循环检查服务状态
service="nginx"
check_count=0
while [ $check_count -lt 5 ]; do
if systemctl is-active --quiet "$service"; then
echo "[$(date)] $service 服务运行正常"
else
echo "[$(date)] $service 服务异常,尝试重启..."
systemctl restart "$service"
fi
check_count=$((check_count + 1))
sleep 60
done
实战案例:系统监控脚本
案例一:CPU和内存监控脚本
#!/bin/bash
# 系统资源监控脚本
# 作者:系统管理员
# 用途:实时监控服务器资源使用情况
report_file="/var/log/system_monitor_$(date +%Y%m%d).log"
# 创建日志目录(如果不存在)
mkdir -p /var/log
# 获取系统负载
uptime_info=$(uptime)
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
memory_info=$(free -h | grep "Mem" | awk '{print $3 "/" $2 " (" $5 ")"}')
disk_usage=$(df -h / | tail -1 | awk '{print $5 " used, " $4 " available"}')
# 生成报告
report="
====================================
系统监控报告
时间: $(date '+%Y-%m-%d %H:%M:%S')
====================================
CPU使用率: ${cpu_usage}%
内存使用: ${memory_info}
磁盘使用: 根分区 ${disk_usage}
系统负载: ${uptime_info}
====================================
"
# 输出到屏幕
echo "$report"
# 保存到日志文件
echo "$report" >> "$report_file"
# 设置告警阈值
if (( $(echo "$cpu_usage > 80" | bc -l) )); then
echo "警告:CPU使用率超过80%!" >> "$report_file"
fi
if echo "$disk_usage" | grep -q "[8-9][0-9]%" || echo "$disk_usage" | grep -q "100%"; then
echo "警告:磁盘使用率超过80%!" >> "$report_file"
fi
echo "监控报告已保存到: $report_file"
案例二:日志分析脚本
#!/bin/bash
# 日志分析脚本
# 用于分析nginx访问日志
log_file="/var/log/nginx/access.log"
output_file="/tmp/log_analysis_report.txt"
if [ ! -f "$log_file" ]; then
echo "错误:日志文件不存在: $log_file"
exit 1
fi
echo "日志分析报告" > "$output_file"
echo "生成时间: $(date)" >> "$output_file"
echo "================================" >> "$output_file"
# 统计总请求数
total_requests=$(wc -l < "$log_file")
echo "总请求数: $total_requests" >> "$output_file"
# 统计IP访问频率
echo "" >> "$output_file"
echo "TOP 10 访问IP:" >> "$output_file"
awk '{print $1}' "$log_file" | sort | uniq -c | sort -rn | head -10 >> "$output_file"
# 统计HTTP状态码
echo "" >> "$output_file"
echo "HTTP状态码统计:" >> "$output_file"
awk '{print $9}' "$log_file" | sort | uniq -c | sort -rn >> "$output_file"
# 统计请求最多的页面
echo "" >> "$output_file"
echo "TOP 10 访问页面:" >> "$output_file"
awk '{print $7}' "$log_file" | sort | uniq -c | sort -rn | head -10 >> "$output_file"
# 统计带宽使用情况
echo "" >> "$output_file"
echo "带宽统计:" >> "$output_file"
total_bytes=$(awk '{sum+=$10} END {print sum}' "$log_file")
echo "总传输字节数: $total_bytes" >> "$output_file"
echo "总传输大小: $(echo "scale=2; $total_bytes/1024/1024" | bc) MB" >> "$output_file"
echo "分析报告已生成: $output_file"
cat "$output_file"
进阶技巧:函数和参数处理
函数定义和使用
函数可以帮助我们组织代码,提高脚本的可读性和可维护性。
#!/bin/bash
# 使用函数的Shell脚本示例
# 定义函数
check_disk_usage() {
local threshold=${1:-80} # 默认阈值80%
local usage=$(df -h / | tail -1 | awk '{print $5}' | cut -d'%' -f1)
if [ "$usage" -gt "$threshold" ]; then
echo "警告:磁盘使用率 ${usage}% 超过阈值 ${threshold}%"
return 1
else
echo "正常:磁盘使用率 ${usage}%"
return 0
fi
}
check_memory_usage() {
local threshold=${1:-90}
local total=$(free -m | grep "Mem" | awk '{print $2}')
local used=$(free -m | grep "Mem" | awk '{print $3}')
local usage_percent=$((used * 100 / total))
if [ "$usage_percent" -gt "$threshold" ]; then
echo "警告:内存使用率 ${usage_percent}% 超过阈值 ${threshold}%"
return 1
else
echo "正常:内存使用率 ${usage_percent}%"
return 0
fi
}
# 主程序
echo "系统健康检查开始..."
echo "时间: $(date)"
echo "===================="
check_disk_usage 75
disk_result=$?
check_memory_usage 85
memory_result=$?
if [ $disk_result -eq 1 ] || [ $memory_result -eq 1 ]; then
echo "===================="
echo "检查发现异常,请及时处理!"
exit 1
else
echo "===================="
echo "所有检查项正常"
exit 0
fi
命令行参数处理
处理命令行参数是编写实用脚本的重要技能。
#!/bin/bash
# 参数处理示例脚本
usage() {
echo "用法: $0 [选项]"
echo "选项:"
echo " -h, --help 显示帮助信息"
echo " -v, --verbose 详细模式"
echo " -t, --threshold 设置阈值(默认80)"
echo " -l, --log 输出到日志文件"
exit 1
}
verbose=false
threshold=80
log_file=""
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
-v|--verbose)
verbose=true
shift
;;
-t|--threshold)
threshold=$2
shift 2
;;
-l|--log)
log_file="/tmp/monitor_$(date +%Y%m%d_%H%M%S).log"
shift
;;
*)
echo "未知选项: $1"
usage
;;
esac
done
# 执行监控
if $verbose; then
echo "详细模式启动,阈值: $threshold"
fi
if [ -n "$log_file" ]; then
exec > "$log_file" 2>&1
echo "日志输出到: $log_file"
fi
# 实际监控逻辑
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
echo "CPU使用率: ${cpu_usage}%"
if (( $(echo "$cpu_usage > $threshold" | bc -l) )); then
echo "警告:CPU使用率超过阈值!"
else
echo "CPU使用率正常"
fi
自动化运维最佳实践
备份脚本模板
#!/bin/bash
# 自动备份脚本
# 配置参数
backup_dir="/backup"
source_dirs=("/etc" "/home" "/var/www")
retention_days=30
date_stamp=$(date +%Y%m%d_%H%M%S)
backup_file="backup_${date_stamp}.tar.gz"
# 创建备份目录
mkdir -p "${backup_dir}/${date_stamp}"
# 执行备份
for dir in "${source_dirs[@]}"; do
if [ -d "$dir" ]; then
echo "正在备份: $dir"
tar -czf "${backup_dir}/${date_stamp}/$(basename $dir).tar.gz" "$dir"
if [ $? -eq 0 ]; then
echo "备份成功: $dir"
else
echo "备份失败: $dir"
fi
else
echo "目录不存在,跳过: $dir"
fi
done
# 清理旧备份
find "$backup_dir" -type d -mtime +${retention_days} -exec rm -rf {} \;
echo "备份完成,时间: $(date)"
服务监控脚本
#!/bin/bash
# 关键服务监控脚本
services=("nginx" "mysql" "redis")
alert_email="admin@example.com"
log_file="/var/log/service_monitor.log"
monitor_service() {
local service=$1
local status=$(systemctl is-active $service)
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if [ "$status" != "active" ]; then
echo "[$timestamp] 警告:服务 $service 状态异常: $status" | tee -a "$log_file"
# 尝试重启服务
echo "[$timestamp] 正在重启服务: $service" | tee -a "$log_file"
systemctl restart $service
# 等待几秒后检查状态
sleep 5
new_status=$(systemctl is-active $service)
if [ "$new_status" != "active" ]; then
echo "[$timestamp] 严重:服务 $service 重启失败,请手动检查!" | tee -a "$log_file"
# 这里可以添加告警通知逻辑
else
echo "[$timestamp] 成功:服务 $service 重启成功" | tee -a "$log_file"
fi
else
echo "[$timestamp] 正常:服务 $service 运行正常" | tee -a "$log_file"
fi
}
# 主监控循环
while true; do
for service in "${services[@]}"; do
monitor_service $service
done
sleep 300 # 每5分钟检查一次
done
调试技巧与常见问题
调试方法
- 使用set命令:
#!/bin/bash
set -x # 开启调试模式
# 你的脚本内容
set +x # 关闭调试模式
- 使用echo命令跟踪变量:
echo "DEBUG: 当前变量值为: $variable"
- 使用bash调试选项:
bash -x script.sh
常见错误及解决方案
错误1:语法错误
# 错误示例
if [ $x = 5 ]
echo "x等于5"
fi
# 正确示例
if [ $x = 5 ]; then
echo "x等于5"
fi
错误2:变量引用问题
# 错误示例
echo $varname
# 正确示例(推荐始终使用引号)
echo "$varname"
错误3:权限问题
# 确保脚本有执行权限
chmod +x script.sh
总结与学习建议
学习Shell脚本需要理论与实践相结合。我建议的学习路径是:
- 从简单开始:先掌握基本的语法和命令
- 多动手实践:编写各种小脚本来解决实际问题
- 学习调试技巧:学会如何快速定位和解决问题
- 阅读优秀脚本:学习他人的编码风格和技巧
- 持续改进:不断反思和优化自己的脚本
记住,编程是一项技能,需要持续的练习和实践。不要害怕犯错,每个错误都是学习的机会。通过不断实践,你将成为Shell脚本专家,能够自动化完成各种复杂的系统管理任务。
希望这些实用的案例和技巧能够帮助你在Linux自动化运维的道路上越走越远!
