使用CURL Shell脚本发邮件部分收件人未收到附件的问题排查
Shell脚本通过CURL发邮件:部分收件人丢失附件的问题分析
问题背景
我通过Shell脚本实现自动化发送带附件的邮件,流程如下:
- 使用
tabcmd命令从私有Tableau服务器下载仪表盘并保存到本地目录 - 通过私有SMTP服务器,调用CURL将下载的文件发送给对应收件人
环境信息:
- Tableau Server与SMTP服务器均部署在CentOS 7容器中
- CURL版本为7.29
- 脚本遍历仪表盘URL列表和对应收件邮箱列表,针对每个仪表盘完成下载后发送给指定收件人
当前问题:部分收件人能正常收到附件,但另一些仅收到邮件正文,附件丢失。CURL返回状态正常,邮件已进入SMTP队列,且本地目录中确实存在对应的附件文件。自身邮箱测试无问题,但替换为客户邮箱后出现差异,且无法直接用客户邮箱复现问题。
核心代码
发送邮件函数send_pdf
send_pdf(){ receiver="$1" subject="$2" message="$3" num_file_attaches=$(expr $# - 3) eval body=$(cat parameters/data.txt) for file in "${@:4}"; do add_file "$body" "$file" done body+="--MULTIPART-MIXED-BOUNDARY--" echo -e "$body" | curl -s "$SMTP_SERVER" \ --mail-from "$SENDER" \ --mail-rcpt "$receiver"\ -T - }
添加附件函数add_file
add_file() { body="$1" file_attach="$2" name_file_attach=$(basename $file_attach) file_base64=$(cat $file_attach | base64) MIMEType=`file --mime-type "$file_attach" | sed 's/.*: //'` body+="--MULTIPART-MIXED-BOUNDARY\nContent-Type: $MIMEType;name=${name_file_attach}\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment;filename=${name_file_attach}\n\n\n${file_base64}\n\n\n" }
可能的问题原因
- Shell函数变量传递错误:这是最关键的问题——
add_file函数中接收的body是值传递,函数内部对body的修改不会同步到send_pdf函数中的全局body变量。也就是说,所有附件的内容根本没被添加到最终的邮件正文中,仅当测试场景无附件或存在变量传递巧合时才会表现正常。 - MIME格式不规范:
- 边界字符串
MULTIPART-MIXED-BOUNDARY无唯一性,部分邮件服务器/客户端对边界唯一性要求严格,重复边界会导致附件解析失败 - 头信息与内容之间的换行符不符合标准(当前用了
\n\n\n),正确的MIME格式要求头信息后用单个空行分隔内容
- 边界字符串
- Base64编码兼容性问题:
base64命令默认将编码内容按每行76字符换行,部分老旧邮件客户端无法正确解析带换行的Base64附件;另外如果tabcmd下载文件未完成就读取,会导致Base64编码残缺,附件无法识别 - MIME类型识别偏差:
file --mime-type可能将PDF文件识别为application/octet-stream而非标准的application/pdf,部分邮件客户端会忽略非预期MIME类型的附件 - 邮箱服务商规则差异:不同企业/公共邮箱的反垃圾邮件、MIME解析规则不同,部分服务商对不符合严格标准的邮件会自动过滤附件
复现与验证方法
- 修复变量传递问题后测试:修改
add_file函数,让它返回附件的MIME片段,再拼接到全局body中,示例修改如下:
用该版本脚本测试之前出问题的客户邮箱,看是否解决附件丢失问题add_file() { file_attach="$1" name_file_attach=$(basename "$file_attach") file_base64=$(base64 "$file_attach") MIMEType=$(file --mime-type "$file_attach" | sed 's/.*: //') echo -e "--MULTIPART-MIXED-BOUNDARY\nContent-Type: $MIMEType;name=\"${name_file_attach}\"\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment;filename=\"${name_file_attach}\"\n\n${file_base64}\n" } # 在send_pdf中修改拼接逻辑 body=$(cat parameters/data.txt) for file in "${@:4}"; do body+=$(add_file "$file") done body+="--MULTIPART-MIXED-BOUNDARY--" - 模拟多样化测试环境:使用不同服务商的邮箱(企业邮箱、Gmail、Outlook、QQ邮箱等)进行批量测试,重点覆盖之前出现问题的邮箱类型
- 验证文件读取完整性:在
tabcmd下载完成后添加延迟(如sleep 3)或检查文件大小是否稳定,再执行发送操作,排除文件未完全写入导致的编码问题 - 构造不规范MIME格式:故意使用重复边界、错误换行符、残缺Base64编码,测试是否会触发附件丢失的情况,验证格式规范对解析的影响
内容的提问来源于stack exchange,提问作者spiderman
相关产品推荐
相关产品推荐

