You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用subprocess调用msmtp发送长邮件时异常:邮件仅含主题无正文

使用subprocess调用msmtp发送长邮件时异常:邮件仅含主题无正文

我之前碰到过一模一样的问题!这大概率是subprocess的管道缓冲或者进程等待机制在搞鬼——直接用shell管道(cat ... | msmtp -t)没问题,但用Python的subprocess手动处理时,很容易因为没处理好数据传输的完整性,导致长邮件正文丢失。

问题根源

当你用Popen启动msmtp后,如果不等它完全读取完所有输入内容就关闭stdin或者让脚本退出,msmtp可能还没来得及处理缓冲区里的正文数据就被中断了:短邮件内容少,缓冲区一次性就传完了,所以没问题;但长邮件内容多,数据还在缓冲区里没被msmtp读取,就被你关闭了stdin,自然只剩主题(主题在邮件头部,是先被读取的)。而shell的管道会自动处理数据传输的完整性,直到所有内容都发送给msmtp才会结束进程。

解决方案

推荐两种可靠的处理方式:

方式一:用communicate()方法(最省心)

communicate()是subprocess专门用来处理进程输入输出的方法,它会一次性把所有数据传给进程,然后等待进程完全结束,自动处理缓冲和进程同步问题,代码简洁又可靠:

import subprocess

with open("email_file.txt", "rb") as email_file:
    # 同时捕获stderr方便排查错误
    p = subprocess.Popen(
        ('msmtp', '-t'),
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
    stdout, stderr = p.communicate(email_file.read())
    
    # 检查发送是否成功
    if p.returncode != 0:
        print(f"邮件发送失败,错误信息:{stderr.decode('utf-8')}")
    else:
        print("邮件发送成功")

方式二:逐行写入时强制刷新缓冲+等待进程结束

如果一定要保持逐行写入的逻辑,那必须手动刷新缓冲区,并且等待msmtp进程处理完所有数据再结束:

import subprocess

p = subprocess.Popen(
    ('msmtp', '-t'),
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)

with open("email_file.txt", "r", encoding='utf-8') as email_file_handle:
    for line in email_file_handle:
        p.stdin.write(line.encode('utf-8'))
        p.stdin.flush()  # 强制把缓冲区的内容立刻发送给msmtp,避免滞留

p.stdin.close()
p.wait()  # 等待msmtp进程完全处理完所有数据并退出

# 检查结果
if p.returncode != 0:
    print(f"邮件发送失败,错误信息:{p.stderr.read().decode('utf-8')}")

为什么你原来的代码不行?

你之前的两种写法都没处理好进程同步:

  • 第一种直接用stdin=open("email_file.txt"),虽然文件对象会在进程结束后关闭,但如果脚本没等待msmtp进程完成就退出,可能导致文件被提前关闭;
  • 第二种逐行写入后直接关闭stdin,但没刷新缓冲,也没等待进程结束,msmtp还没读完所有正文就被终止了。

备注:内容来源于stack exchange,提问作者MikeyMike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 17:49:30