Bash脚本中日志与错误处理功能的正确配置方法
报错根因
- 命令替换的反引号配对/边界错误:直接在exec重定向行内写反引号包裹的date命令时,很容易出现边界错位,shell解析到行尾都找不到闭合的反引号,直接抛出匹配反引号失败的EOF错误
- 原date格式包含空格、冒号等特殊字符:
%T输出的时分秒带冒号,日期和时间之间有空格,未加引号包裹的情况下,shell会把空格后的内容识别为独立参数,即使反引号配对正确也会触发执行错误 - 原exec仅重定向标准输出,执行报错信息不会写入日志,后续排查问题无据可依
功能实现方案
1. 日期后缀日志配置
- 提前单独定义日志路径变量,不在exec行内直接嵌套命令替换,彻底规避反引号转义问题
- 日期格式选用文件名兼容的格式:每日定时任务用
%Y%m%d(年月日)作为后缀即可实现按天拆分日志不覆盖;如需区分单日多次执行,可精确到秒,用下划线替换空格、横杠替换冒号避免特殊字符解析问题 - exec同时重定向标准输出、标准错误到日志文件,所有执行trace、报错信息全量落盘
- 执行前自动创建日志目录,避免路径不存在导致重定向失败
2. SFTP失败邮件告警
- 逐环节捕获操作返回值:cd目录、cp备份、sftp传输任意环节失败,立刻触发告警,跳过后续无效操作,避免误删本地文件
- 告警邮件包含核心排查信息:失败时间、操作用户、对应ID、日志路径,收到告警可直接定位问题
- 仅当sftp传输确认成功后,再删除本地txt文件,避免文件丢失
修复后可直接使用的完整脚本:
#!/bin/bash # 配置项 LOG_DIR="/root/logs" CSV_PATH="/绝对路径/us_craig.csv" # 替换为csv文件的绝对路径,避免定时任务工作目录不对找不到文件 ALERT_MAIL="你的告警接收邮箱@xxx.com" SFTP_KEY="/root/.ssh/id_rsa" SFTP_TARGET="user@site.domain.com:/home/folder" # 初始化日志:按天生成日志文件,不会覆盖历史 mkdir -p "$LOG_DIR" LOG_DATE=$(date '+%Y%m%d') # 精确到秒可改为'+%Y%m%d_%H-%M-%S' LOG_FILE="${LOG_DIR}/sftp_transfer_${LOG_DATE}.log" exec > "$LOG_FILE" 2>&1 set -x echo "=== SFTP传输任务开始执行,时间:$(date '+%Y-%m-%d %H:%M:%S') ===" while IFS="," read -r col1 col2 do echo "=== 开始处理用户:$col1,对应ID:$col2 ===" TARGET_DIR="/efs/$col1/ftpout" BACKUP_DIR="/efs/$col1/backup" # 校验工作目录存在 if [ ! -d "$TARGET_DIR" ]; then echo "错误:用户$col1 的ftpout目录不存在,跳过" # 发送告警邮件 { echo "From: SFTP任务告警 <sftp-alert@localhost>" echo "To: 运维负责人 <${ALERT_MAIL}>" echo "Subject: 【严重】SFTP传输失败 - 用户${col1}" echo "" echo "SFTP任务执行失败,详情:" echo "用户:$col1" echo "ID:$col2" echo "失败原因:工作目录$TARGET_DIR 不存在" echo "失败时间:$(date '+%Y-%m-%d %H:%M:%S')" echo "日志路径:$LOG_FILE" } | sendmail -t continue fi # 切换目录 cd "$TARGET_DIR" || { echo "错误:切换到目录$TARGET_DIR 失败,跳过" { echo "From: SFTP任务告警 <sftp-alert@localhost>" echo "To: 运维负责人 <${ALERT_MAIL}>" echo "Subject: 【严重】SFTP传输失败 - 用户${col1}" echo "" echo "SFTP任务执行失败,详情:" echo "用户:$col1" echo "ID:$col2" echo "失败原因:切换到工作目录$TARGET_DIR 失败" echo "失败时间:$(date '+%Y-%m-%d %H:%M:%S')" echo "日志路径:$LOG_FILE" } | sendmail -t continue } # 创建备份目录 mkdir -p "$BACKUP_DIR" # 备份文件 cp * "$BACKUP_DIR" || { echo "错误:备份文件到$BACKUP_DIR 失败,跳过" { echo "From: SFTP任务告警 <sftp-alert@localhost>" echo "To: 运维负责人 <${ALERT_MAIL}>" echo "Subject: 【严重】SFTP传输失败 - 用户${col1}" echo "" echo "SFTP任务执行失败,详情:" echo "用户:$col1" echo "ID:$col2" echo "失败原因:文件备份到$BACKUP_DIR 失败" echo "失败时间:$(date '+%Y-%m-%d %H:%M:%S')" echo "日志路径:$LOG_FILE" } | sendmail -t continue } # 执行SFTP传输 sftp -oIdentityfile="$SFTP_KEY" "$SFTP_TARGET" <<EOF put *.txt exit EOF SFTP_EXIT_CODE=$? if [ $SFTP_EXIT_CODE -ne 0 ]; then echo "错误:SFTP传输失败,返回码:$SFTP_EXIT_CODE" { echo "From: SFTP任务告警 <sftp-alert@localhost>" echo "To: 运维负责人 <${ALERT_MAIL}>" echo "Subject: 【严重】SFTP传输失败 - 用户${col1}" echo "" echo "SFTP任务执行失败,详情:" echo "用户:$col1" echo "ID:$col2" echo "失败原因:SFTP文件传输失败,返回码$SFTP_EXIT_CODE" echo "失败时间:$(date '+%Y-%m-%d %H:%M:%S')" echo "日志路径:$LOG_FILE" } | sendmail -t continue fi # 传输成功才删除本地文件 rm *.txt echo "=== 用户$col1 处理完成 ===" done < <(tail -n +2 "$CSV_PATH") echo "=== SFTP传输任务全部执行完成,时间:$(date '+%Y-%m-%d %H:%M:%S') ==="
注意:使用前请先确认服务器sendmail服务已正常启动并配置好邮件路由,可先手动执行
echo "test" | mail -s "test" 你的邮箱验证邮件发送能力。
内容的提问来源于stack exchange,提问作者Craig
相关产品推荐
相关产品推荐

