摘要:Shell脚本是Linux系统管理和自动化运维的核心工具。本文基于实际生产环境中的备份脚本案例,深入讲解Shell脚本的高级使用技巧,包括变量处理、错误处理、参数传递、定时任务集成等关键知识点,并通过真实案例进行印证。
详细内容请参考下文。
一、变量高级技巧
1.命令替换与变量嵌套
基础用法:
# 反引号(旧式)
date_str=`date '+%Y%m%d'`
# $(...) 推荐用法(支持嵌套)
date_str=$(date '+%Y%m%d')
实战案例(基于前面的tar命令):

2.变量默认值与替换
# 设置默认值
BACKUP_DIR=${BACKUP_DIR:-/var/backup}
DB_NAME=${1:-"bpm"}# 参数默认值
# 变量非空时使用
LOG_FILE=${LOG_FILE:="/var/log/backup.log"}
# 变量为空时报错
BACKUP_PATH=${BACKUP_PATH:?"错误:未设置备份路径"}
3.数组的高级应用
bash
#!/bin/bash
# 定义要备份的数据库列表
DATABASES=("bpm" "hr" "oa" "sso")
# 遍历数组
for db in "${DATABASES[@]}"; do
echo"正在备份数据库: $db"
mysqldump$db > "/backup/${db}_$(date +%Y%m%d).sql"
done
# 获取数组长度
echo "总共需要备份 ${#DATABASES[@]} 个数据库"
二、错误处理与调试技巧
1.严格模式(建议每个脚本都加上)
bash
#!/bin/bash
# 遇到任何错误立即退出
set -e
# 使用未定义的变量时报错
set -u
# 管道命令中任何一个失败都算失败
set -o pipefail
# 等同于:set -euo pipefail
实战案例:
2.手动错误处理
#!/bin/bash
backup_database() {
localdb_name=$1
localbackup_file="/backup/${db_name}_$(date +%Y%m%d).sql"
# 执行备份并检查返回值
ifmysqldump "$db_name" > "$backup_file" 2>/dev/null; then
echo"[SUCCESS] $db_name 备份成功"
return0
else
echo"[ERROR] $db_name 备份失败" >&2
return1
fi
}
# 调用并处理错误
backup_database "bpm" || {
echo"备份失败,发送告警邮件"
echo"BPM备份失败" | mail -s "备份告警" admin@example.com
exit1
}
3.脚本调试模式
# 方法1:脚本内开启调试
#!/bin/bash
set -x# 打印每条执行的命令
# ... 脚本内容
set +x# 关闭调试
# 方法2:执行时开启调试
bash -x bpmbackup.sh
# 方法3:只调试部分代码
#!/bin/bash
DEBUG=${DEBUG:-false}
if [ "$DEBUG" = "true" ]; then
set-x
fi
# ... 脚本内容
三、函数高级技巧
1.函数定义与参数处理

2.递归函数与局部变量

四、文件操作高级技巧
1.安全的临时文件处理
#!/bin/bash
# 创建临时文件/目录
temp_file=$(mktemp)
temp_dir=$(mktemp -d)
# 设置退出时自动清理
trap "rm -rf '$temp_file' '$temp_dir'" EXIT INT TERM
# 使用临时文件
mysqldump bpm > "$temp_file"
tar -zcvf backup.tar.gz -C "$temp_dir" .
echo "临时文件已自动清理"
2.文件锁防止并发执行
#!/bin/bash
LOCK_FILE="/var/run/bpmbackup.lock"
# 尝试获取锁
exec 200>"$LOCK_FILE"
if ! flock -n 200; then
echo"错误:备份脚本已在运行中" >&2
exit1
fi
# 备份逻辑
echo "开始备份..."
tar -zcvf "bpm_$(date +%Y%m%d).tar.gz" "bpm_$(date +%Y%m%d).sql" --remove-files
# 释放锁
flock -u 200
五、日志记录高级技巧
1.带时间戳的日志函数
说明:带时间戳的日志函数是指在脚本中自定义一个函数(如 log_info、log_error),每次记录日志时自动在消息前加上当前的日期和时间使每一条日志都有明确的产生时刻,方便后续追踪问题发生的时间点、分析事件顺序以及定位故障。

2. 日志轮转
说明:日志轮转是指定期将当前日志文件重命名、压缩并归档,同时创建新的空日志文件继续记录,以防止单个日志文件过大占用磁盘空间或导致性能问题。

六、定时任务集成技巧
1.crontab定时任务的环境适配
#!/bin/bash
# bpmbackup.sh -备份脚本
# 设置PATH(cron环境PATH很短)
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# 设置语言环境
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
# 获取脚本所在目录(使用绝对路径)
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
BACKUP_DIR="/var/backup"
LOG_FILE="/var/log/bpmbackup.log"
# 记录开始时间
echo "=========================================" >> "$LOG_FILE"
echo "备份开始: $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE"
# 执行备份
date_str=$(date '+%Y%m%d')
cd "$BACKUP_DIR" || exit 1
if tar -zcvf "bpm_${date_str}.tar.gz" "bpm_${date_str}.sql" --remove-files >> "$LOG_FILE" 2>&1; then
echo"备份成功: bpm_${date_str}.tar.gz" >> "$LOG_FILE"
else
echo"备份失败!" >> "$LOG_FILE"
exit1
fi
echo "备份结束: $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE"
2.crontab定时任务的配置示例

七、性能优化技巧
1.并行处理
#!/bin/bash
# 并行压缩多个文件
parallel_compress() {
localdir=$1
localfiles=$(find "$dir" -name "*.sql" -type f)
#最多同时运行4个压缩任务
echo"$files" | xargs -P 4 -I {} tar -zcvf {}.tar.gz {} --remove-files
}
2. 使用管道减少磁盘I/O
# 不创建中间文件,直接压缩
mysqldump bpm | gzip > "bpm_$(date +%Y%m%d).sql.gz"
# 或使用tar直接打包
mysqldump bpm | tar -zcvf "bpm_$(date +%Y%m%d).tar.gz"
八、生产环境完整案例
1.完整的数据库备份脚本
#!/bin/bash
# bpmbackup.sh - 数据库备份脚本
set -euo pipefail
# ========== 配置区域 ==========
BACKUP_BASE_DIR="/data/backup"
DB_NAME="bpm"
DB_USER="backup_user"
DB_PASS="your_password"
RETENTION_DAYS=30
LOG_FILE="/var/log/bpmbackup.log"
LOCK_FILE="/var/run/bpmbackup.lock"
MAX_BACKUP_SIZE_MB=10240# 10GB
# ========== 函数定义 ==========
log() {
locallevel=$1
shift
echo"[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" | tee -a "$LOG_FILE"
}
cleanup_old_backups() {
localbackup_dir=$1
localdays=$2
log"INFO" "清理 ${days} 天前的备份文件..."
find"$backup_dir" -name "*.tar.gz" -type f -mtime +$days -delete
find"$backup_dir" -name "*.sql" -type f -mtime +$days -delete
}
check_disk_space() {
localdir=$1
localavailable=$(df -m "$dir" | awk 'NR==2 {print $4}')
if[ "$available" -lt 1024 ]; then# 小于1GB
log"ERROR" "磁盘空间不足,剩余 ${available}MB"
return1
fi
log"INFO" "磁盘空间充足,剩余 ${available}MB"
}
compress_and_cleanup() {
localdate_str=$1
localsql_file="${BACKUP_BASE_DIR}/${DB_NAME}_${date_str}.sql"
localtar_file="${BACKUP_BASE_DIR}/${DB_NAME}_${date_str}.tar.gz"
if[ ! -f "$sql_file" ]; then
log"ERROR" "SQL文件不存在: $sql_file"
return1
fi
#检查文件大小
localsql_size=$(stat -c%s "$sql_file")
if[ "$sql_size" -eq 0 ]; then
log"ERROR" "SQL文件为空: $sql_file"
return1
fi
#压缩并删除原文件
log"INFO" "正在压缩: $sql_file"
tar-zcvf "$tar_file" -C "$BACKUP_BASE_DIR" "${DB_NAME}_${date_str}.sql" --remove-files
if[ $? -eq 0 ]; then
log"SUCCESS" "压缩完成: $tar_file"
#验证压缩包
iftar -tzf "$tar_file" > /dev/null 2>&1; then
log"SUCCESS" "压缩包验证通过"
else
log"ERROR" "压缩包损坏!"
return1
fi
else
log"ERROR" "压缩失败"
return1
fi
}
# ========== 主程序 ==========
main() {
#防止并发执行
exec200>"$LOCK_FILE"
if! flock -n 200; then
log"ERROR" "脚本已在运行中"
exit1
fi
log"INFO" "========== BPM备份开始 =========="
#检查目录
mkdir-p "$BACKUP_BASE_DIR"
check_disk_space"$BACKUP_BASE_DIR" || exit 1
#执行备份
DATE_STR=$(date'+%Y%m%d')
#这里假设SQL文件已通过其他方式生成
#实际可以是: mysqldump -u$DB_USER -p$DB_PASS $DB_NAME > ${BACKUP_BASE_DIR}/${DB_NAME}_${DATE_STR}.sql
#压缩并清理
compress_and_cleanup"$DATE_STR"
#清理旧备份
cleanup_old_backups"$BACKUP_BASE_DIR" "$RETENTION_DAYS"
log"INFO" "========== BPM备份完成 =========="
#释放锁
flock-u 200
}
# 执行主程序
Main
2. crontab配置定时任务
