如何设置深度学习任务结束后邮件提醒

0 阅读6分钟

首先安装环境

设置非交互模式,使用默认配置

sudo DEBIAN_FRONTEND=noninteractive apt-get update

安装postfix(自动选择Local only配置)

sudo DEBIAN_FRONTEND=noninteractive apt-get install -y postfix

安装mailutils(会复用postfix配置)

sudo DEBIAN_FRONTEND=noninteractive apt-get install -y mailutils


创建email-reminder文件夹 在这个文件夹里创建4个文件,分别是email_config.sh, run_task.sh, send_email.py, test_email_simple.sh

其中email_config.sh内容如下:

export QQ_EMAIL="XX"                    # XX修改为你的QQ邮箱
export QQ_PASSWORD="YY"                 # YY修改为你的授权码
export RECIPIENT="XX"                   # 收件人邮箱,通常和发件邮箱相同,只是起到提醒作用

run_task.sh内容如下:


# 加载邮箱配置
source email-reminder/email_config.sh  
# 检查命令行参数
if [ $# -ne 1 ]; then
    echo "用法: $0 <任务脚本路径>"
    echo "示例: $0 /zengke/test2.sh"
    exit 1
fi

# 获取任务脚本路径
TASK_SCRIPT=$1

# 检查脚本是否存在
if [ ! -f "$TASK_SCRIPT" ]; then
    echo "错误: 脚本 '$TASK_SCRIPT' 不存在"
    exit 1
fi

# 自动赋予脚本可执行权限
echo "正在为脚本 '$TASK_SCRIPT' 添加可执行权限..."
chmod +x "$TASK_SCRIPT"
if [ $? -ne 0 ]; then
    echo "警告: 无法为脚本 '$TASK_SCRIPT' 添加可执行权限,但将尝试继续执行"
fi

# 设置时区为北京时间
export TZ='Asia/Shanghai'
# 注意:时间将在后续步骤中额外增加8小时以确保显示正确

# 记录开始时间戳
start_timestamp=$(date +%s)
# 获取开始时间并加上8小时
start_time=$(date -d "+8 hours" '+%Y-%m-%d %H:%M:%S %Z')
# start_time=$(date '+%Y-%m-%d %H:%M:%S %Z')
task_name="$TASK_SCRIPT"

echo "=========================================="
echo "任务开始执行: $start_time"
echo "执行脚本: $task_name"
echo "=========================================="

# 执行指定的任务脚本
bash "$TASK_SCRIPT"

# 检查执行状态
if [ $? -eq 0 ]; then
    status="成功"
else
    status="失败"
fi

# 记录结束时间戳
end_timestamp=$(date +%s)
# 获取结束时间并加上8小时
end_time=$(date -d "+8 hours" '+%Y-%m-%d %H:%M:%S %Z')
# end_time=$(date '+%Y-%m-%d %H:%M:%S %Z')
duration=$((end_timestamp - start_timestamp))

# 格式化耗时
hours=$((duration / 3600))
minutes=$(( (duration % 3600) / 60 ))
seconds=$((duration % 60))
duration_str=$(printf "%02d小时%02d分钟%02d秒" $hours $minutes $seconds)

# 构建邮件内容
subject="任务完成"
message="任务名称: $task_name
开始时间: $start_time
结束时间: $end_time
任务耗时: $duration_str
执行状态: $status"


echo ""
echo "=========================================="
echo "任务执行完成"
echo "状态: $status"
echo "开始时间: $start_time"
echo "结束时间: $end_time"
echo "任务耗时: $duration_str"
echo "=========================================="
echo ""

# 发送邮件
echo "📧 正在发送邮件通知..."
python3 /zengke/e-mail-remind/send_email.py "$subject" "$message" "$RECIPIENT" "$QQ_EMAIL" "$QQ_PASSWORD"

if [ $? -eq 0 ]; then
    echo "✅ 邮件发送成功!"
else
    echo "❌ 邮件发送失败"
fi

echo ""
echo "🎉 任务流程执行完毕"

send_email.py内容如下:

# -*- coding: utf-8 -*-

import smtplib
import sys
import os
import socket
import time
# # 导入时区相关模块,确保Python使用正确的时区
# import datetime
# import pytz
from email.mime.text import MIMEText
from email.header import Header

# 设置环境时区为上海,确保Python脚本使用正确的时区
os.environ['TZ'] = 'Asia/Shanghai'
# # 确保datetime使用正确的时区
# def get_beijing_time():
#     return datetime.datetime.now(pytz.timezone('Asia/Shanghai'))

def send_qq_email(subject, message, recipient, sender_email, sender_password):
    """
    使用QQ邮箱发送邮件
    
    Args:
        subject: 邮件主题
        message: 邮件内容
        recipient: 收件人邮箱
        sender_email: 发件人QQ邮箱
        sender_password: QQ邮箱授权码
    """
    try:
        # 打印诊断信息
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 开始发送邮件...")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 诊断信息:")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - 发送方: {sender_email}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - 接收方: {recipient}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - SMTP服务器: smtp.qq.com:587")
        
        # 尝试获取本地主机名和IP地址
        try:
            hostname = socket.gethostname()
            local_ip = socket.gethostbyname(hostname)
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - 本地主机名: {hostname}")
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - 本地IP地址: {local_ip}")
        except Exception as e:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - 获取本地网络信息失败: {e}")
        
        # 创建邮件
        msg = MIMEText(message, 'plain', 'utf-8')
        msg['Subject'] = Header(subject, 'utf-8')
        msg['From'] = sender_email
        msg['To'] = recipient
        
        # 连接QQ邮箱SMTP服务器(使用非SSL连接,稍后升级到TLS)
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 正在连接QQ邮箱SMTP服务器...")
        
        # 尝试不同的连接策略
        try:
            # 方法1:不指定source_address,让系统自动选择
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 尝试连接方法1: 让系统自动选择本地地址")
            server = smtplib.SMTP('smtp.qq.com', 587, timeout=30)
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 连接方法1成功建立初始连接")
        except Exception as e1:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 连接方法1失败: {e1}")
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 尝试连接方法2: 使用明确的本地地址")
            # 方法2:使用特定的source_address
            server = smtplib.SMTP('smtp.qq.com', 587, source_address=('', 0), timeout=30)
        
        # 尝试设置socket选项(在某些Python版本中,SMTP对象可能没有直接的socket属性)
        try:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 尝试访问socket对象...")
            # 不同Python版本中SMTP对象的socket属性名称可能不同
            if hasattr(server, '_socket'):
                print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 使用_socket属性设置选项")
                server._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                sockname = server._socket.getsockname()
                print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 本地socket绑定地址: {sockname}")
            elif hasattr(server, 'socket'):
                print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 使用socket属性设置选项")
                server.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                sockname = server.socket.getsockname()
                print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 本地socket绑定地址: {sockname}")
            else:
                print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] SMTP对象没有直接的socket属性访问方式")
                print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 这通常不会影响邮件发送功能")
        except Exception as e:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] socket操作失败: {e}")
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 注意:socket配置错误通常不会阻止邮件发送,继续尝试...")
        
        # 升级连接到TLS加密
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 升级连接到TLS加密...")
        server.starttls()
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] TLS加密升级成功")
        
        # 登录QQ邮箱
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 登录QQ邮箱...")
        server.login(sender_email, sender_password)
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] QQ邮箱登录成功")
        
        # 发送邮件
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送邮件...")
        server.sendmail(sender_email, [recipient], msg.as_string())
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 邮件发送完成")
        server.quit()
        
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] ✅ 邮件发送成功!")
        return True
        
    except smtplib.SMTPAuthenticationError:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] ❌ 认证失败,请检查:")
        print("   - QQ邮箱地址是否正确")
        print("   - 授权码是否正确(不是QQ密码)")
        print("   - 是否已开启SMTP服务")
        return False
        
    except socket.error as se:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] ❌ 网络连接错误: {se}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 网络错误详情:")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - 错误类型: {type(se).__name__}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - 错误编号: {se.errno}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - 错误信息: {se.strerror}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 可能的解决方案:")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 1. 检查网络连接和防火墙设置")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 2. 确认SMTP服务器地址和端口正确")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 3. 检查系统网络配置")
        return False
        
    except Exception as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] ❌ 邮件发送失败: {e}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 错误详情:")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] - 错误类型: {type(e).__name__}")
        return False

def main():
    if len(sys.argv) != 6:
        print("用法: python3 send_email.py <主题> <内容> <收件人> <发件人> <授权码>")
        print("示例: python3 send_email.py '测试' '内容' '123@qq.com' '123@qq.com' '授权码'")
        sys.exit(1)
    
    subject = sys.argv[1]
    message = sys.argv[2]
    recipient = sys.argv[3]
    sender_email = sys.argv[4]
    sender_password = sys.argv[5]
    
    success = send_qq_email(subject, message, recipient, sender_email, sender_password)
    sys.exit(0 if success else 1)

if __name__ == "__main__":
    main()

用法 bash run_task.sh XX.sh,其中XX.sh是你要运行的脚本

test_email_simple.sh的内容如下:


# 加载配置
source /email-reminder/email_config.sh

echo "=== QQ邮箱发送测试 ==="
echo "发件人: $QQ_EMAIL"
echo "收件人: $RECIPIENT"
echo "测试时间: $(date -d "+8 hours" '+%Y-%m-%d %H:%M:%S')"

# 测试邮件内容
subject="QQ邮箱配置测试"
message="这是一封QQ邮箱配置测试邮件。

如果收到此邮件,说明配置成功!"

# 测试时间: $(date -d "+8 hours" '+%Y-%m-%d %H:%M:%S')
# 服务器: $(hostname)"

# 使用独立的Python脚本发送邮件
python3 /email-reminder/send_email.py "$subject" "$message" "$RECIPIENT" "$QQ_EMAIL" "$QQ_PASSWORD"

可以先运行bash test_email_simple.sh,如果邮箱收到了测试邮件,说明已经配置成功。