2026复习shell模板 - 以及shell不同场景shell脚本和语法复习
本文提供了一个实用的Shell脚本模板,包含两个版本:简单版适用于日常运维任务,加长版具备更完善的错误处理和日志功能。两个版本都采用严格模式(set -euo pipefail)确保安全性,包含自动日志记录、参数解析、帮助文档等核心功能。加长版额外增加了锁机制防止重复执行、信号捕获、彩色日志输出、依赖检查等高级特性。脚本模板结构清晰,配置区与业务逻辑分离,可直接用于服务器检查、监控和维护等场景。使
背景
AI时代还是会用到shell脚本,作为技术人。
如果白天被同事问道某个技术点自己忘记了,一定会复习一下,复习的同时学一篇博客,完善一下之前的shell 模板吧~
如果是模板前两个基本够用。
如果其他场景 搜索关键字即可
脚本模板 - 简单版 - 平时用修改主函数即可
#!/usr/bin/env bash
set -euo pipefail # 严格模式:错误退出、未定义变量报错、管道错误传播
# ====== 配置区 ======
SCRIPT_NAME=$(basename "$0")
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
LOG_FILE="${SCRIPT_DIR}/${SCRIPT_NAME%.*}.log"
# ====== 日志函数 ======
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# ====== 帮助信息 ======
show_help() {
cat << EOF
${SCRIPT_NAME} - 日常运维脚本
用法: ${SCRIPT_NAME} [选项] [参数]
选项:
-h 显示此帮助信息
-f FILE 指定配置文件
-d DIR 指定工作目录
-v 详细输出模式
-t 测试模式(不执行实际操作)
示例:
${SCRIPT_NAME} -f config.ini
${SCRIPT_NAME} -d /opt/app -v
EOF
exit 0
}
# ====== 参数解析 ======
parse_args() {
local work_dir=""
local config_file=""
local verbose=false
local test_mode=false
while getopts "hf:d:vt" opt; do
case $opt in
h) show_help ;;
f) config_file="$OPTARG" ;;
d) work_dir="$OPTARG" ;;
v) verbose=true ;;
t) test_mode=true ;;
?) show_help ;;
esac
done
shift $((OPTIND-1))
# 设置工作目录
if [ -n "$work_dir" ]; then
if [ -d "$work_dir" ]; then
cd "$work_dir" || { log "错误:无法切换到目录 $work_dir"; exit 1; }
log "切换到工作目录: $(pwd)"
else
log "错误:目录不存在 $work_dir"
exit 1
fi
fi
return 0
}
# ====== 主函数 ======
main() {
log "脚本开始执行: ${SCRIPT_NAME}"
# 解析参数
parse_args "$@"
# 业务逻辑示例
log "执行系统检查..."
# 1. 检查磁盘空间
local disk_usage=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$disk_usage" -gt 80 ]; then
log "警告:磁盘使用率 ${disk_usage}% > 80%"
fi
# 2. 检查内存
local mem_free=$(free -m | awk '/Mem:/ {printf "%.1f", $4/$2*100}')
log "可用内存百分比: ${mem_free}%"
# 3. 进程检查(示例:检查Nginx)
if pgrep nginx > /dev/null; then
log "服务状态: Nginx 正在运行"
else
log "服务状态: Nginx 未运行"
fi
log "脚本执行完成"
}
# ====== 脚本入口 ======
if [[ "${BASH_SOURCE[0]}" = "${0}" ]]; then
main "$@"
fi
使用示例:
# 查看帮助
./daily_ops.sh -h
# 切换到指定目录执行
./daily_ops.sh -d /var/log
# 详细模式
./daily_ops.sh -v
# 测试模式
./daily_ops.sh -t
# 组合使用
./daily_ops.sh -d /opt/app -f config.ini -v
脚本特点:
- 严格模式:
set -euo pipefail确保安全性 - 自动日志:所有输出同时显示并记录到文件
- 目录切换:安全的目录切换和验证
- 完整帮助:标准的 -h 参数支持
- 参数解析:使用 getopts 处理选项
- 简洁实用:50行以内,覆盖日常运维基本需求
这个脚本包含了运维脚本中最常用的功能,可以直接用于日常的服务器检查、监控、维护等任务。
覆盖常见语法以及兼顾幂等性 通用脚本模板 - 加长版
关键点:
严格模式:set -euo pipefail 防止错误继续执行
锁机制:防止脚本重复执行
信号捕获:优雅处理中断信号
彩色日志:更好地区分日志级别
模块化函数:更清晰的代码结构
完善错误处理:每个函数都有返回值检查
配置分离:关键配置集中在顶部
依赖检查:确保运行环境正常
更安全的变量引用:使用双引号和花括号
这个模板覆盖了运维脚本的常见需求,可以直接用于实际工作。
#!/usr/bin/env bash
set -euo pipefail # 添加:严格模式,遇到错误立即退出
# ====== 配置区域 ======
readonly SCRIPT_NAME=$(basename "$0")
readonly SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
readonly LOG_DIR="${SCRIPT_DIR}/logs"
readonly LOCK_FILE="/tmp/${SCRIPT_NAME}.lock"
# 颜色定义
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m' # No Color
# ====== 日志函数 ======
init_log() {
mkdir -p "$LOG_DIR"
local log_date=$(date '+%Y%m%d')
LOG_FILE="${LOG_DIR}/${SCRIPT_NAME}.${log_date}.log"
}
log() {
local level=$1
local message=$2
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case $level in
"INFO") color=$GREEN ;;
"WARN") color=$YELLOW ;;
"ERROR") color=$RED ;;
*) color=$NC ;;
esac
echo -e "${color}[${timestamp}] [${level}] ${message}${NC}"
echo "[${timestamp}] [${level}] ${message}" >> "$LOG_FILE"
}
# ====== 锁机制(防止重复执行) ======
check_lock() {
if [ -f "$LOCK_FILE" ]; then
local pid=$(cat "$LOCK_FILE")
if ps -p "$pid" > /dev/null 2>&1; then
log "ERROR" "脚本已在运行 (PID: $pid)"
exit 1
else
rm -f "$LOCK_FILE"
fi
fi
echo $$ > "$LOCK_FILE"
}
cleanup() {
rm -f "$LOCK_FILE"
log "INFO" "脚本执行结束,清理完成"
}
# ====== 业务函数 ======
function check_dependencies() {
local deps=("nginx" "redis-cli" "awk")
for dep in "${deps[@]}"; do
if ! command -v "$dep" &> /dev/null; then
log "ERROR" "依赖命令不存在: $dep"
return 1
fi
done
}
function backup_config() {
local config_file="$1"
local backup_dir="${SCRIPT_DIR}/backup"
local timestamp=$(date '+%Y%m%d_%H%M%S')
mkdir -p "$backup_dir"
cp "$config_file" "${backup_dir}/$(basename "$config_file").${timestamp}.bak"
if [ $? -eq 0 ]; then
log "INFO" "配置文件备份成功: $config_file"
else
log "ERROR" "配置文件备份失败"
return 1
fi
}
function service_control() {
local service_name="$1"
local action="$2"
if systemctl list-unit-files | grep -q "${service_name}.service"; then
if systemctl "$action" "${service_name}.service"; then
log "INFO" "服务 ${service_name} ${action} 成功"
return 0
else
log "ERROR" "服务 ${service_name} ${action} 失败"
return 1
fi
else
log "WARN" "服务 ${service_name} 不存在"
return 2
fi
}
# ====== 主逻辑 ======
main() {
trap cleanup EXIT INT TERM # 设置信号捕获
check_lock
init_log
log "INFO" "脚本开始执行"
# 检查依赖
check_dependencies || exit 1
# 幂等性检查示例
local target_dir="/etc/nginx/conf.d"
if [ ! -d "$target_dir" ]; then
log "ERROR" "目标目录不存在: $target_dir"
exit 1
fi
# 备份原有配置(幂等性:重复执行不会产生问题)
backup_config "/etc/nginx/nginx.conf"
# 服务操作
service_control "nginx" "restart"
# 验证服务状态
if systemctl is-active --quiet nginx.service; then
log "INFO" "Nginx服务运行正常"
else
log "ERROR" "Nginx服务异常"
exit 1
fi
log "INFO" "所有操作执行完成"
}
# ====== 参数解析 ======
parse_args() {
while getopts "hu:p:s:" opt; do
case $opt in
h) help; exit 0 ;;
u) username="$OPTARG" ;;
p) password="$OPTARG" ;;
s)
case $OPTARG in
"phy") echo "物理CPU数: $(get_phy_cpu)" ;;
"cores") echo "每个CPU核数: $(get_cores)" ;;
"pro") echo "逻辑CPU数: $(get_pro_cpu)" ;;
*) echo "无效参数"; help ;;
esac
exit 0
;;
?) help; exit 1 ;;
esac
done
}
help() {
cat << EOF
${SCRIPT_NAME} - 运维部署脚本
用法: ${SCRIPT_NAME} [选项]
选项:
-h 显示此帮助信息
-u USER 指定用户名
-p PASSWORD 指定密码
-s TYPE 系统信息类型: phy/cores/pro
示例:
${SCRIPT_NAME} -s phy
${SCRIPT_NAME} -u admin -p password
EOF
}
# ====== 工具函数 ======
get_phy_cpu() {
grep -c "physical id" /proc/cpuinfo | sort -u
}
get_cores() {
grep "cpu cores" /proc/cpuinfo | uniq | awk -F: '{print $2}' | tr -d ' '
}
get_pro_cpu() {
grep -c processor /proc/cpuinfo
}
# ====== 脚本入口 ======
if [[ $# -eq 0 ]]; then
help
exit 1
fi
parse_args "$@"
main
Shell脚本编程:从入门到高级运维实战指南
前言
在Linux运维的世界里,Shell脚本无疑是最高效的"瑞士军刀"。无论是日常的系统维护、批量操作,还是复杂的自动化部署,掌握Shell脚本都是每个运维工程师的必备技能。本文将带你从基础语法开始,逐步深入高级运维实战场景。
第一部分:Shell脚本基础篇
1.1 Shell脚本是什么?
Shell脚本本质上是一个文本文件,包含一系列Shell命令,可以被Shell解释器执行。它让重复性的系统管理工作变得自动化和标准化。
1.2 你的第一个Shell脚本
#!/bin/bash
# 这是一个注释
echo "Hello, World!"
保存为hello.sh,然后赋予执行权限:
chmod +x hello.sh
./hello.sh
1.3 变量和数据类型
#!/bin/bash
# 变量定义和使用
name="运维工程师"
age=30
PI=3.14159
echo "姓名: $name"
echo "年龄: ${age}岁" # 花括号让变量边界更清晰
# 环境变量
echo "当前用户: $USER"
echo "家目录: $HOME"
1.4 基本控制结构
条件判断
#!/bin/bash
# if-elif-else结构
read -p "请输入分数(0-100): " score
if [ $score -ge 90 ]; then
echo "优秀"
elif [ $score -ge 80 ]; then
echo "良好"
elif [ $score -ge 60 ]; then
echo "及格"
else
echo "不及格"
fi
# case语句
read -p "请输入操作(start/stop/restart): " action
case $action in
start)
echo "开始服务"
;;
stop)
echo "停止服务"
;;
restart)
echo "重启服务"
;;
*)
echo "未知操作"
exit 1
;;
esac
循环结构
#!/bin/bash
# for循环
echo "=== for循环示例 ==="
for i in {1..5}; do
echo "第 $i 次循环"
done
# 遍历数组
services=("nginx" "mysql" "redis")
for service in "${services[@]}"; do
echo "检查服务: $service"
done
# while循环
echo "=== while循环示例 ==="
count=1
while [ $count -le 5 ]; do
echo "计数: $count"
((count++))
done
# 无限循环(常用于监控)
while true; do
if ping -c 1 google.com &> /dev/null; then
echo "网络正常 $(date)"
else
echo "网络异常 $(date)"
fi
sleep 10 # 等待10秒
done
1.5 函数基础
#!/bin/bash
# 函数定义和调用
function say_hello() {
local name=$1 # local表示局部变量
echo "Hello, $name!"
}
# 带返回值的函数
check_service() {
local service_name=$1
if systemctl is-active --quiet "$service_name"; then
return 0 # 成功
else
return 1 # 失败
fi
}
# 调用函数
say_hello "读者"
if check_service "nginx"; then
echo "Nginx正在运行"
else
echo "Nginx未运行"
fi
第二部分:Shell脚本进阶篇
2.1 参数处理和选项解析
#!/bin/bash
# getopts参数解析
usage() {
echo "用法: $0 [-h] [-v] [-f file] [-u user]"
echo " -h 显示帮助"
echo " -v 详细输出"
echo " -f file 指定配置文件"
echo " -u user 指定用户名"
exit 1
}
# 默认值
verbose=false
config_file=""
username=""
# 解析参数
while getopts "hvf:u:" opt; do
case $opt in
h) usage ;;
v) verbose=true ;;
f) config_file="$OPTARG" ;;
u) username="$OPTARG" ;;
?) usage ;;
esac
done
# 剩余参数
shift $((OPTIND-1))
if $verbose; then
echo "详细模式已开启"
fi
if [ -n "$config_file" ]; then
echo "配置文件: $config_file"
fi
if [ $# -gt 0 ]; then
echo "额外参数: $@"
fi
2.2 文件操作和文本处理
#!/bin/bash
# 文件操作示例
check_and_backup() {
local source_file=$1
local backup_dir="/backup"
# 检查文件是否存在
if [ ! -f "$source_file" ]; then
echo "错误: 文件不存在 - $source_file" >&2
return 1
fi
# 检查备份目录
mkdir -p "$backup_dir"
# 创建带时间戳的备份
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="${backup_dir}/$(basename $source_file).${timestamp}.bak"
cp "$source_file" "$backup_file"
# 验证备份
if cmp -s "$source_file" "$backup_file"; then
echo "备份成功: $backup_file"
return 0
else
echo "备份失败" >&2
return 1
fi
}
# 文本处理示例
process_log() {
local log_file=$1
local output_file=$2
# 提取错误日志并统计
echo "=== 错误日志分析 ===" > "$output_file"
echo "时间范围: $(date)" >> "$output_file"
echo "==================" >> "$output_file"
# 统计不同错误类型
grep -i "error" "$log_file" | \
awk '{count[$4]++} END {for (type in count) print type ": " count[type]}' \
>> "$output_file"
# 提取最近10条错误
echo -e "\n=== 最近10条错误 ===" >> "$output_file"
grep -i "error" "$log_file" | tail -10 >> "$output_file"
}
2.3 错误处理和调试
#!/bin/bash
# 启用严格模式
set -euo pipefail
# 错误处理函数
error_exit() {
echo "错误: $1" >&2
exit 1
}
# 清理函数
cleanup() {
echo "执行清理操作..."
# 删除临时文件
rm -f /tmp/temp_*.log
# 停止相关进程等
echo "清理完成"
}
# 注册清理函数
trap cleanup EXIT INT TERM
# 调试函数
debug() {
if [ "${DEBUG:-false}" = "true" ]; then
echo "[DEBUG] $1" >&2
fi
}
# 示例:安全执行命令
safe_exec() {
local cmd="$@"
debug "执行命令: $cmd"
if eval "$cmd"; then
debug "命令执行成功"
return 0
else
error_exit "命令执行失败: $cmd"
fi
}
# 使用示例
main() {
debug "开始主函数"
# 必须存在的目录检查
local required_dirs=("/etc/nginx" "/var/log")
for dir in "${required_dirs[@]}"; do
if [ ! -d "$dir" ]; then
error_exit "目录不存在: $dir"
fi
done
# 安全执行命令
safe_exec "ls -la /etc/nginx"
debug "主函数结束"
}
# 设置调试模式
export DEBUG=true
main
第三部分:运维实战场景
3.1 系统监控和告警
#!/bin/bash
# 系统资源监控脚本
set -euo pipefail
# 配置
readonly THRESHOLD_CPU=80
readonly THRESHOLD_MEM=85
readonly THRESHOLD_DISK=90
readonly ALERT_EMAIL="admin@example.com"
readonly LOG_FILE="/var/log/system_monitor.log"
# 初始化日志
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 检查CPU使用率
check_cpu() {
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
cpu_usage=${cpu_usage%.*} # 取整
if [ "$cpu_usage" -gt "$THRESHOLD_CPU" ]; then
log "警告: CPU使用率过高 - ${cpu_usage}%"
send_alert "CPU" "$cpu_usage"
return 1
fi
return 0
}
# 检查内存使用率
check_memory() {
local mem_total=$(free -m | awk '/Mem:/ {print $2}')
local mem_used=$(free -m | awk '/Mem:/ {print $3}')
local mem_percent=$((mem_used * 100 / mem_total))
if [ "$mem_percent" -gt "$THRESHOLD_MEM" ]; then
log "警告: 内存使用率过高 - ${mem_percent}%"
send_alert "内存" "$mem_percent"
return 1
fi
return 0
}
# 检查磁盘使用率
check_disk() {
while IFS= read -r line; do
local usage=$(echo "$line" | awk '{print $5}' | sed 's/%//')
local partition=$(echo "$line" | awk '{print $6}')
if [ "$usage" -gt "$THRESHOLD_DISK" ]; then
log "警告: 磁盘分区 $partition 使用率过高 - ${usage}%"
send_alert "磁盘($partition)" "$usage"
fi
done < <(df -h | grep '^/dev/')
}
# 检查服务状态
check_services() {
local services=("nginx" "mysql" "redis" "docker")
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service"; then
log "服务正常: $service"
else
log "错误: 服务 $service 未运行"
send_alert "服务" "$service 停止"
# 尝试重启
log "尝试重启服务: $service"
if systemctl restart "$service"; then
log "服务重启成功: $service"
else
log "服务重启失败: $service"
fi
fi
done
}
# 发送告警
send_alert() {
local resource=$1
local value=$2
local subject="[系统告警] ${resource} 异常"
local body="资源: ${resource}\n当前值: ${value}%\n服务器: $(hostname)\n时间: $(date)"
echo -e "$body" | mail -s "$subject" "$ALERT_EMAIL"
}
# 生成报告
generate_report() {
echo "=== 系统监控报告 $(date) ==="
echo "主机名: $(hostname)"
echo "运行时间: $(uptime -p)"
echo ""
echo "CPU信息:"
top -bn1 | grep "Cpu(s)"
echo ""
echo "内存信息:"
free -h
echo ""
echo "磁盘信息:"
df -h
echo ""
echo "最近5个高进程:"
ps aux --sort=-%cpu | head -6
}
# 主函数
main() {
log "开始系统监控检查"
check_cpu
check_memory
check_disk
check_services
# 每小时生成一次详细报告
if [ "$(date +%M)" = "00" ]; then
generate_report >> "$LOG_FILE"
fi
log "系统监控检查完成"
}
# 设置定时任务运行
main
3.2 自动化部署脚本
#!/bin/bash
# 应用自动化部署脚本
set -euo pipefail
# 配置
readonly APP_NAME="myapp"
readonly APP_USER="appuser"
readonly DEPLOY_DIR="/opt/${APP_NAME}"
readonly BACKUP_DIR="/backup/${APP_NAME}"
readonly LOG_FILE="/var/log/deploy_${APP_NAME}.log"
readonly GIT_REPO="https://github.com/example/myapp.git"
readonly BRANCH="main"
# 颜色输出
readonly GREEN='\033[0;32m'
readonly RED='\033[0;31m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m'
log() {
local level=$1
local msg=$2
case $level in
"INFO") color=$GREEN ;;
"ERROR") color=$RED ;;
"WARN") color=$YELLOW ;;
*) color=$NC ;;
esac
echo -e "${color}[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $msg${NC}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $msg" >> "$LOG_FILE"
}
# 检查前置条件
check_prerequisites() {
log "INFO" "检查部署前置条件"
# 检查用户是否存在
if ! id "$APP_USER" &>/dev/null; then
log "ERROR" "用户 $APP_USER 不存在"
return 1
fi
# 检查必要命令
local required_cmds=("git" "tar" "systemctl" "python3")
for cmd in "${required_cmds[@]}"; do
if ! command -v "$cmd" &>/dev/null; then
log "ERROR" "命令不存在: $cmd"
return 1
fi
done
# 检查目录权限
local required_dirs=("/opt" "/backup")
for dir in "${required_dirs[@]}"; do
if [ ! -w "$dir" ]; then
log "ERROR" "目录不可写: $dir"
return 1
fi
done
log "INFO" "前置条件检查通过"
}
# 备份当前版本
backup_current() {
log "INFO" "开始备份当前版本"
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_path="${BACKUP_DIR}/${timestamp}"
mkdir -p "$backup_path"
if [ -d "$DEPLOY_DIR" ]; then
cp -r "$DEPLOY_DIR"/* "$backup_path/"
log "INFO" "备份完成: $backup_path"
# 清理旧备份(保留最近7天)
find "$BACKUP_DIR" -type d -mtime +7 -exec rm -rf {} \;
else
log "WARN" "部署目录不存在,跳过备份"
fi
}
# 拉取代码
pull_code() {
log "INFO" "开始拉取代码"
if [ -d "$DEPLOY_DIR/.git" ]; then
cd "$DEPLOY_DIR"
git fetch origin
git checkout "$BRANCH"
git reset --hard "origin/$BRANCH"
else
git clone -b "$BRANCH" "$GIT_REPO" "$DEPLOY_DIR"
fi
# 记录提交信息
cd "$DEPLOY_DIR"
local commit_hash=$(git rev-parse --short HEAD)
local commit_msg=$(git log -1 --pretty=%B)
log "INFO" "代码拉取完成"
log "INFO" "提交哈希: $commit_hash"
log "INFO" "提交信息: $commit_msg"
echo "$commit_hash" > "${DEPLOY_DIR}/REVISION"
}
# 安装依赖
install_dependencies() {
log "INFO" "安装依赖"
cd "$DEPLOY_DIR"
# Python依赖
if [ -f "requirements.txt" ]; then
pip3 install -r requirements.txt
fi
# NPM依赖
if [ -f "package.json" ]; then
npm install --production
fi
# 设置文件权限
chown -R "${APP_USER}:${APP_USER}" "$DEPLOY_DIR"
find "$DEPLOY_DIR" -type f -name "*.sh" -exec chmod +x {} \;
}
# 数据库迁移
run_migrations() {
log "INFO" "执行数据库迁移"
if [ -f "${DEPLOY_DIR}/manage.py" ]; then
cd "$DEPLOY_DIR"
sudo -u "$APP_USER" python3 manage.py migrate --noinput
fi
log "INFO" "数据库迁移完成"
}
# 重启服务
restart_service() {
log "INFO" "重启应用服务"
local service_name="${APP_NAME}.service"
if systemctl is-enabled "$service_name" &>/dev/null; then
systemctl restart "$service_name"
sleep 5
if systemctl is-active --quiet "$service_name"; then
log "INFO" "服务重启成功"
else
log "ERROR" "服务重启失败"
return 1
fi
else
log "WARN" "服务未启用,跳过重启"
fi
}
# 健康检查
health_check() {
log "INFO" "开始健康检查"
local max_attempts=10
local attempt=1
local health_url="http://localhost:8080/health"
while [ $attempt -le $max_attempts ]; do
if curl -s -f "$health_url" &>/dev/null; then
log "INFO" "应用健康检查通过"
return 0
fi
log "WARN" "健康检查失败,尝试 $attempt/$max_attempts"
sleep 10
((attempt++))
done
log "ERROR" "健康检查失败,达到最大重试次数"
return 1
}
# 回滚操作
rollback() {
log "ERROR" "开始回滚操作"
# 找到最新的备份
local latest_backup=$(find "$BACKUP_DIR" -type d -name "20*" | sort -r | head -1)
if [ -d "$latest_backup" ]; then
log "INFO" "回滚到备份: $latest_backup"
# 清空当前目录
rm -rf "${DEPLOY_DIR:?}/*"
# 恢复备份
cp -r "$latest_backup"/* "$DEPLOY_DIR/"
# 重启服务
restart_service
log "INFO" "回滚完成"
else
log "ERROR" "找不到可用备份"
return 1
fi
}
# 主部署流程
deploy() {
log "INFO" "开始部署 $APP_NAME"
# 检查前置条件
if ! check_prerequisites; then
exit 1
fi
# 执行部署步骤
local steps=(
"backup_current"
"pull_code"
"install_dependencies"
"run_migrations"
"restart_service"
"health_check"
)
for step in "${steps[@]}"; do
log "INFO" "执行步骤: $step"
if ! $step; then
log "ERROR" "步骤 $step 执行失败"
# 尝试回滚
if rollback; then
log "INFO" "回滚成功"
else
log "ERROR" "回滚失败"
fi
exit 1
fi
done
log "INFO" "部署完成!"
# 发送部署通知
send_notification "success" "应用 $APP_NAME 部署成功"
}
# 发送通知
send_notification() {
local status=$1
local message=$2
# 可以集成到各种通知系统
# Slack、钉钉、邮件、企业微信等
echo "发送部署通知: $status - $message"
# 示例:发送邮件
# echo "$message" | mail -s "部署通知: $APP_NAME $status" "team@example.com"
}
# 使用说明
usage() {
cat << EOF
Usage: $0 [command]
Commands:
deploy 执行部署
rollback 回滚到上一个版本
status 查看部署状态
help 显示此帮助信息
Examples:
$0 deploy
$0 rollback
$0 status
EOF
}
# 命令行参数处理
main() {
local command=${1:-help}
case "$command" in
deploy)
deploy
;;
rollback)
rollback
;;
status)
echo "当前版本: $(cat ${DEPLOY_DIR}/REVISION 2>/dev/null || echo '未知')"
systemctl status "${APP_NAME}.service" || true
;;
help)
usage
;;
*)
echo "未知命令: $command"
usage
exit 1
;;
esac
}
# 脚本入口
if [[ $# -gt 0 ]]; then
main "$@"
else
usage
fi
3.3 日志分析和报告生成
#!/bin/bash
# 日志分析脚本
set -euo pipefail
analyze_nginx_logs() {
local log_file=$1
local report_file=$2
echo "=== Nginx访问日志分析报告 ===" > "$report_file"
echo "分析时间: $(date)" >> "$report_file"
echo "日志文件: $log_file" >> "$report_file"
echo "=================================" >> "$report_file"
# 总请求数
local total_requests=$(wc -l < "$log_file")
echo "总请求数: $total_requests" >> "$report_file"
# 请求方法统计
echo -e "\n--- 请求方法统计 ---" >> "$report_file"
awk '{print $6}' "$log_file" | \
sed 's/"//g' | \
sort | uniq -c | sort -rn >> "$report_file"
# 状态码统计
echo -e "\n--- HTTP状态码统计 ---" >> "$report_file"
awk '{print $9}' "$log_file" | \
sort | uniq -c | sort -rn >> "$report_file"
# 最频繁的IP
echo -e "\n--- 最频繁访问IP (Top 10) ---" >> "$report_file"
awk '{print $1}' "$log_file" | \
sort | uniq -c | sort -rn | head -10 >> "$report_file"
# 最受欢迎的URL
echo -e "\n--- 最受欢迎URL (Top 10) ---" >> "$report_file"
awk '{print $7}' "$log_file" | \
sort | uniq -c | sort -rn | head -10 >> "$report_file"
# 错误请求分析
echo -e "\n--- 错误请求分析 (4xx/5xx) ---" >> "$report_file"
awk '$9 ~ /^[45]/ {print $9, $7, $1}' "$log_file" | \
head -20 >> "$report_file"
# 流量统计
echo -e "\n--- 流量统计 ---" >> "$report_file"
awk '{sum += $10} END {printf "总流量: %.2f MB\n", sum/1024/1024}' \
"$log_file" >> "$report_file"
echo -e "\n报告生成完成: $report_file"
}
第四部分:最佳实践和高级技巧
4.1 Shell脚本最佳实践
-
启用严格模式
set -euo pipefail -
使用函数和模块化
- 每个函数只做一件事
- 函数长度不超过50行
-
完善的错误处理
trap 'cleanup; exit 1' ERR -
可读性和可维护性
- 使用有意义的变量名
- 添加注释和文档
- 统一的代码风格
-
安全性考虑
# 避免使用eval # 验证用户输入 # 最小权限原则
4.2 性能优化技巧
-
减少子进程创建
# 不好:每次循环都创建新进程 for file in *.txt; do wc -l "$file" done # 好:一次性处理 wc -l *.txt -
使用内置命令
# 使用shell内置而非外部命令 # 内置: echo, printf, test, [, read # 外部: cat, grep, sed, awk -
避免不必要的管道
# 不好:多个管道 cat file | grep pattern | awk '{print $1}' # 好:单个命令完成 awk '/pattern/ {print $1}' file
4.3 调试技巧
# 调试模式运行
bash -x script.sh
# 在脚本中开启调试
set -x # 开启调试
# 你的代码
set +x # 关闭调试
# 只调试特定部分
echo "调试前"
(
set -x
# 需要调试的代码
command1
command2
)
echo "调试后"
结语
Shell脚本是运维工程师的强大工具,掌握它能够极大提高工作效率。从简单的自动化任务到复杂的部署流程,良好的Shell脚本设计都能让运维工作更加规范、高效。
记住:好的脚本应该是自文档化的、健壮的、可维护的。每次写脚本时,都要考虑到未来的维护者可能就是你自己。
更多推荐



所有评论(0)