如何用Bash提取.eml指定短语后至空行文本并处理=结尾软换行
问题根因
你写的脚本在测试txt上正常、真实eml上失效,两个问题的本质原因都是没有适配eml文件的格式特性:
- 空行停止逻辑失效:真实eml的段落分隔空行往往不是纯零字符空行,要么带Windows换行残留的
\r(CR回车符,用cat -v查看会显示为^M),要么带不可见的空格/制表符,你写的/^$/正则只能匹配完全无任何字符的行,自然识别不到这类分隔行,会一直输出到文件末尾。 - QP软换行合并失效:一是行尾的
=前后可能夹带\r隐藏字符,导致你判断/=$/的正则匹配不到;二是如果把软换行合并和内容提取的顺序写反,没先合并拆分的行就做匹配,逻辑会直接失效。另外macOS自带的sed/awk都是BSD版本,和Linux上的GNU版本语法有差异,照搬Linux教程的写法也会出异常。
可用实现方案
两个方案均适配macOS 12.4自带运行环境,无需额外安装依赖。
方案1:BSD工具链Shell脚本(优先bash兼容)
脚本先统一剔除隐藏回车符,优先完成QP软换行合并,再做关键词匹配和段落提取,空行识别兼容带不可见空白的场景。
#!/bin/bash # 适配macOS 12.4 自带bash 3.2/BSD工具链 if [ $# -lt 2 ]; then echo "用法: $0 <搜索关键词> <eml文件路径1> [eml文件路径2...]" exit 1 fi KEYWORD="$1" shift for eml_file in "$@"; do echo "===== 处理文件: $eml_file =====" # 第一步:删除所有CR回车符,解决隐藏字符问题 tr -d '\r' < "$eml_file" | awk -v kw="$KEYWORD" ' BEGIN { capture = 0 buffer = "" } { # 处理QP软换行拼接 if (buffer != "") { cur_line = buffer $0 buffer = "" } else { cur_line = $0 } # 行尾以=结尾则存入缓冲区,等待下一行拼接 if (sub(/=$/, "", cur_line)) { buffer = cur_line next } # 软换行合并完成后处理内容提取 if (capture == 0) { if (index(cur_line, kw) > 0) { capture = 1 } next } else { # 匹配到全空白行则停止捕获 if (cur_line ~ /^[[:space:]]*$/) { capture = 0 next } print cur_line } } END { # 输出缓冲区剩余的未处理内容 if (buffer != "" && capture == 1) { print buffer } } ' done
方案2:Python脚本(容错性更高,适合大批量处理)
用Python标准库直接处理QP解码,不用手动写换行合并逻辑,对非标准编码的eml兼容性更好。
#!/usr/bin/env python3 import sys import quopri import os def extract_from_eml(keyword, file_path): print(f"===== 处理文件: {file_path} =====") with open(file_path, 'rb') as f: raw = f.read() # 自动解码QP格式、合并软换行 decoded = quopri.decodestring(raw).decode('utf-8', errors='ignore') # 统一换行符格式 lines = decoded.replace('\r\n', '\n').replace('\r', '\n').split('\n') capture = False for line in lines: if not capture: if keyword in line: capture = True continue # 遇到空行停止输出 if not line.strip(): capture = False break print(line) if __name__ == '__main__': if len(sys.argv) < 3: print(f"用法: {sys.argv[0]} <搜索关键词> <eml文件路径1> [路径2...]") sys.exit(1) kw = sys.argv[1] for fpath in sys.argv[2:]: if os.path.isfile(fpath): extract_from_eml(kw, fpath)
使用说明
- 把脚本保存到本地,shell脚本存为
extract_eml.sh,Python脚本存为extract_eml.py,执行chmod +x 脚本文件名添加执行权限 - 调用示例:提取目录下所有eml中包含「报销明细」关键词的对应段落,结果保存到result.txt:
- Shell版执行:
./extract_eml.sh "报销明细" ./你的eml存放目录/*.eml > result.txt - Python版执行:
./extract_eml.py "报销明细" ./你的eml存放目录/*.eml > result.txt
- Shell版执行:
- 如果遇到中文乱码,把Python脚本里
decode('utf-8'的utf-8替换为gb18030即可适配国内部分邮件客户端的编码格式 - 单批次处理上千个eml文件时,Python版的处理速度比Shell版快3~5倍
内容的提问来源于stack exchange,提问作者orchardl
相关产品推荐
相关产品推荐

