如何使用Grep匹配完整邮箱地址而非子串(多文件场景)
问题描述
我有两个文件:
第一个文件(命名为file1.txt)内容如下:
./j/n:jwillow@gmail.com:Test1 ./n/o:nanofranky@list.ru:Test2 ./j/r:franky@list.ru:Test2 ./j/d:jdmq77@hotmail.com:Test3 ./x/s:xsebastianmenendez@hotmail.com:Test4 ./r/s:rsebastianmenendez@hotmail.com:Test5 ./w/i:willow@gmail.com@gmail.com:Test6
第二个文件(命名为file2.txt)是待匹配的完整邮箱列表:
willow@gmail.com franky@list.ru jdmq77@hotmail.com sebastianmenendez@hotmail.com
期望输出匹配到完整邮箱的行:
./j/r:franky@list.ru:Test2 ./j/d:jdmq77@hotmail.com:Test3 ./w/i:willow@gmail.com@gmail.com:Test6
使用grep -f命令时,会匹配到邮箱子串(比如jwillow@gmail.com包含willow@gmail.com),导致返回file1.txt的所有行,无法只保留符合要求的行。
解决方案
方法1:用awk精准处理分隔字段
awk适合处理结构化的分隔文本,我们可以先把待匹配邮箱存入数组,再逐行检查第一个文件的第二个字段(冒号分隔)是否包含目标邮箱,且邮箱的起始位置要么是字段开头,要么是在@之后(避免匹配前缀带多余字符的情况)。
执行命令:
awk -F: 'NR==FNR {mail[$0]; next} {for (m in mail) {if (index($2, m) == 1 || (index($2, m) > 1 && substr($2, index($2, m)-1, 1) == "@")) {print; next}}}' file2.txt file1.txt
命令解释
-F::指定冒号为字段分隔符NR==FNR {mail[$0]; next}:读取file2.txt的所有邮箱,存入数组mail,然后跳过后续逻辑处理下一行- 遍历
file1.txt的每行:- 对每个目标邮箱
m,检查第二个字段$2:- 要么
m从字段开头开始(index($2, m) == 1),比如franky@list.ru完全匹配字段,或willow@gmail.com是willow@gmail.com@gmail.com的前缀 - 要么
m的前一个字符是@(比如xxx@willow@gmail.com这类情况)
- 要么
- 满足任一条件就打印该行,跳过当前行的后续检查
- 对每个目标邮箱
方法2:改造grep的匹配规则
如果坚持用grep,需要给每个邮箱添加正则约束,确保匹配的邮箱不会被前缀字符干扰。先通过sed把邮箱列表转换成带约束的正则,再用grep匹配。
执行命令:
# 生成带正则约束的匹配规则文件 sed 's/^/.*:\([^@]*@\)\?/; s/$/\(:.*\)$/' file2.txt > match_patterns.txt # 执行匹配 grep -f match_patterns.txt file1.txt
命令解释
- sed命令将每个邮箱
m转换为正则表达式.*:\([^@]*@\)\?m:.*$:.*::匹配行首到第一个冒号的内容\([^@]*@\)\?:可选匹配任意非@字符加@,兼容邮箱出现在@之后的情况m:待匹配的目标邮箱:\(.*\)$:匹配邮箱后的冒号及剩余内容
grep -f读取生成的规则文件,精准匹配file1.txt的行
两种方法执行后,都会输出期望的结果:
./j/r:franky@list.ru:Test2 ./j/d:jdmq77@hotmail.com:Test3 ./w/i:willow@gmail.com@gmail.com:Test6
内容的提问来源于stack exchange,提问作者aDoN
相关产品推荐
相关产品推荐

