实战案例
概述
本章通过实际案例展示如何将前面学到的知识应用到日常工作中。
系统管理脚本
系统信息报告
bash
#!/bin/bash
# system-info.sh - 系统信息报告脚本
echo "======================================"
echo " 系统信息报告"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
echo "======================================"
echo -e "\n[系统信息]"
echo "主机名: $(hostname)"
echo "系统: $(uname -s) $(uname -r)"
echo "架构: $(uname -m)"
echo "运行时间: $(uptime -p)"
echo -e "\n[CPU 信息]"
echo "CPU: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2)"
echo "核心数: $(nproc)"
echo "负载: $(cat /proc/loadavg | awk '{print $1, $2, $3}')"
echo -e "\n[内存信息]"
free -h | awk 'NR==2{printf "总计: %s, 已用: %s, 可用: %s\n", $2, $3, $7}'
echo -e "\n[磁盘使用]"
df -h | grep -E '^/dev' | awk '{printf "%-15s %s 已用 (%s)\n", $1, $3, $5}'
echo -e "\n[网络接口]"
ip -4 addr show | grep -E 'inet ' | awk '{print $NF": "$2}'
echo -e "\n[登录用户]"
who | awk '{print $1" 从 "$5" 登录于 "$3" "$4}'日志清理脚本
bash
#!/bin/bash
# clean-logs.sh - 清理旧日志文件
LOG_DIR="/var/log"
DAYS=30
DRY_RUN=false
# 解析参数
while getopts "d:n" opt; do
case $opt in
d) DAYS=$OPTARG ;;
n) DRY_RUN=true ;;
*) echo "用法: $0 [-d days] [-n dry-run]"; exit 1 ;;
esac
done
echo "清理 $DAYS 天前的日志文件..."
echo "目录: $LOG_DIR"
echo "模拟运行: $DRY_RUN"
echo
# 查找旧日志
files=$(find "$LOG_DIR" -type f -name "*.log*" -mtime +$DAYS 2>/dev/null)
if [ -z "$files" ]; then
echo "没有找到需要清理的文件"
exit 0
fi
total_size=0
count=0
while IFS= read -r file; do
size=$(du -b "$file" 2>/dev/null | cut -f1)
total_size=$((total_size + size))
count=$((count + 1))
if [ "$DRY_RUN" = true ]; then
echo "[模拟] 将删除: $file ($(du -h "$file" | cut -f1))"
else
rm -f "$file"
echo "已删除: $file"
fi
done <<< "$files"
echo
echo "共 $count 个文件, 总计 $(numfmt --to=iec $total_size)"用户批量创建
bash
#!/bin/bash
# create-users.sh - 批量创建用户
# 用户列表文件格式: username:password:group
USER_FILE="users.txt"
if [ ! -f "$USER_FILE" ]; then
echo "错误: 找不到 $USER_FILE"
exit 1
fi
while IFS=: read -r username password group; do
# 跳过注释和空行
[[ "$username" =~ ^#.*$ || -z "$username" ]] && continue
# 检查用户是否存在
if id "$username" &>/dev/null; then
echo "用户 $username 已存在,跳过"
continue
fi
# 创建组(如果不存在)
if ! getent group "$group" &>/dev/null; then
groupadd "$group"
echo "创建组: $group"
fi
# 创建用户
useradd -m -g "$group" -s /bin/bash "$username"
echo "$username:$password" | chpasswd
# 强制首次登录修改密码
chage -d 0 "$username"
echo "创建用户: $username (组: $group)"
done < "$USER_FILE"
echo "完成!"备份脚本
增量备份脚本
bash
#!/bin/bash
# incremental-backup.sh - 增量备份脚本
BACKUP_DIR="/backup"
SOURCE_DIR="/var/www"
SNAPSHOT_FILE="${BACKUP_DIR}/.snapshot"
DATE=$(date +%Y%m%d)
mkdir -p "$BACKUP_DIR"
# 确定备份类型
if [ ! -f "$SNAPSHOT_FILE" ]; then
# 完整备份
BACKUP_FILE="${BACKUP_DIR}/full_${DATE}.tar.gz"
echo "执行完整备份..."
tar -czf "$BACKUP_FILE" -g "$SNAPSHOT_FILE" "$SOURCE_DIR"
else
# 增量备份
BACKUP_FILE="${BACKUP_DIR}/incr_${DATE}_$(date +%H%M%S).tar.gz"
echo "执行增量备份..."
tar -czf "$BACKUP_FILE" -g "$SNAPSHOT_FILE" "$SOURCE_DIR"
fi
if [ $? -eq 0 ]; then
echo "备份成功: $BACKUP_FILE"
echo "大小: $(du -h "$BACKUP_FILE" | cut -f1)"
else
echo "备份失败!"
exit 1
fi
# 清理 30 天前的备份
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete
echo "已清理旧备份"数据库备份
bash
#!/bin/bash
# mysql-backup.sh - MySQL 数据库备份
DB_USER="backup_user"
DB_PASS="password"
BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7
mkdir -p "$BACKUP_DIR"
# 获取所有数据库
databases=$(mysql -u"$DB_USER" -p"$DB_PASS" -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|sys)")
for db in $databases; do
BACKUP_FILE="${BACKUP_DIR}/${db}_${DATE}.sql.gz"
echo "备份数据库: $db"
mysqldump -u"$DB_USER" -p"$DB_PASS" --single-transaction "$db" | gzip > "$BACKUP_FILE"
if [ ${PIPESTATUS[0]} -eq 0 ]; then
echo " 成功: $BACKUP_FILE ($(du -h "$BACKUP_FILE" | cut -f1))"
else
echo " 失败: $db"
fi
done
# 清理旧备份
echo "清理 $RETENTION_DAYS 天前的备份..."
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "备份完成!"监控脚本
磁盘空间监控
bash
#!/bin/bash
# disk-monitor.sh - 磁盘空间监控
THRESHOLD=80
ALERT_EMAIL="admin@example.com"
check_disk() {
df -h | grep -E '^/dev' | while read -r line; do
partition=$(echo "$line" | awk '{print $1}')
usage=$(echo "$line" | awk '{print $5}' | tr -d '%')
mount_point=$(echo "$line" | awk '{print $6}')
if [ "$usage" -ge "$THRESHOLD" ]; then
echo "警告: $mount_point ($partition) 使用率 ${usage}%"
# 发送邮件告警
if command -v mail &>/dev/null; then
echo "磁盘告警: $mount_point 使用率 ${usage}%" | \
mail -s "磁盘空间告警 - $(hostname)" "$ALERT_EMAIL"
fi
fi
done
}
echo "磁盘空间检查 - $(date)"
echo "告警阈值: ${THRESHOLD}%"
echo "========================"
check_disk服务监控
bash
#!/bin/bash
# service-monitor.sh - 服务监控脚本
SERVICES=("nginx" "mysql" "redis")
LOG_FILE="/var/log/service-monitor.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
check_and_restart() {
local service=$1
if ! systemctl is-active --quiet "$service"; then
log "警告: $service 已停止,尝试重启..."
systemctl restart "$service"
sleep 5
if systemctl is-active --quiet "$service"; then
log "成功: $service 已重启"
else
log "错误: $service 重启失败!"
fi
fi
}
log "开始服务检查..."
for service in "${SERVICES[@]}"; do
check_and_restart "$service"
done
log "服务检查完成"日志分析
Web 日志分析
bash
#!/bin/bash
# analyze-access-log.sh - 分析 Nginx 访问日志
LOG_FILE="/var/log/nginx/access.log"
TOP_N=10
echo "=== Web 日志分析 ==="
echo "日志文件: $LOG_FILE"
echo "分析时间: $(date)"
echo
echo "--- 访问量最高的 IP (Top $TOP_N) ---"
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -n $TOP_N
echo
echo "--- 访问最多的页面 (Top $TOP_N) ---"
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -n $TOP_N
echo
echo "--- HTTP 状态码统计 ---"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn
echo
echo "--- 每小时访问量 ---"
awk '{print $4}' "$LOG_FILE" | cut -d: -f2 | sort | uniq -c
echo
echo "--- 流量统计 ---"
total_bytes=$(awk '{sum += $10} END {print sum}' "$LOG_FILE")
echo "总流量: $(numfmt --to=iec $total_bytes 2>/dev/null || echo "$total_bytes bytes")"部署脚本
简单部署脚本
bash
#!/bin/bash
# deploy.sh - 应用部署脚本
APP_NAME="myapp"
DEPLOY_DIR="/var/www/$APP_NAME"
REPO_URL="git@github.com:user/myapp.git"
BRANCH="main"
BACKUP_DIR="/backup/deployments"
set -e # 出错即退出
log() {
echo "[$(date '+%H:%M:%S')] $1"
}
# 创建备份
backup() {
if [ -d "$DEPLOY_DIR" ]; then
local backup_file="${BACKUP_DIR}/${APP_NAME}_$(date +%Y%m%d_%H%M%S).tar.gz"
mkdir -p "$BACKUP_DIR"
log "创建备份: $backup_file"
tar -czf "$backup_file" -C "$(dirname $DEPLOY_DIR)" "$(basename $DEPLOY_DIR)"
fi
}
# 拉取代码
pull_code() {
if [ -d "$DEPLOY_DIR/.git" ]; then
log "更新代码..."
cd "$DEPLOY_DIR"
git fetch origin
git checkout "$BRANCH"
git pull origin "$BRANCH"
else
log "克隆仓库..."
git clone -b "$BRANCH" "$REPO_URL" "$DEPLOY_DIR"
fi
}
# 安装依赖
install_deps() {
cd "$DEPLOY_DIR"
if [ -f "package.json" ]; then
log "安装 Node.js 依赖..."
npm install --production
elif [ -f "requirements.txt" ]; then
log "安装 Python 依赖..."
pip install -r requirements.txt
fi
}
# 重启服务
restart_service() {
log "重启服务..."
systemctl restart "$APP_NAME"
}
# 主流程
main() {
log "开始部署 $APP_NAME..."
backup
pull_code
install_deps
restart_service
log "部署完成!"
}
main实用单行命令
bash
# 查找大文件
find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null
# 统计代码行数
find . -name "*.py" | xargs wc -l | tail -1
# 批量重命名文件
for f in *.txt; do mv "$f" "${f%.txt}.md"; done
# 监控命令输出变化
watch -n 1 "ss -s"
# 查找并删除空目录
find . -type d -empty -delete
# 显示目录大小并排序
du -sh */ | sort -h
# 查找最近修改的文件
find . -type f -mtime -1 -ls
# 批量压缩图片
for f in *.jpg; do convert "$f" -quality 80 "compressed_$f"; done
# 生成随机密码
openssl rand -base64 16
# 检查端口是否开放
timeout 1 bash -c '</dev/tcp/localhost/80' && echo "开放" || echo "关闭"小结
本章展示了多个实用的 Shell 脚本案例:
- 系统管理:信息报告、日志清理、用户管理
- 备份脚本:增量备份、数据库备份
- 监控脚本:磁盘监控、服务监控
- 日志分析:Web 日志统计
- 部署脚本:自动化部署
这些脚本可以根据实际需求进行修改和扩展。通过实践,你将更好地掌握 Shell 脚本编程。
上一章:环境变量
返回:目录