Bash awk筛选具有有效电子邮件地址的行
我对bash和awk还不熟悉,我花了好几天的时间试图学习它。我认为我非常接近解决方案,但还没有完全解决。所以,请求你的帮助。请注意,我不希望使用grep,因为我发现它要慢得多 我有大量的文本文件,每个都有几百MB的大小。不幸的是,它们在任何一种格式中都没有完全标准化。加上这里有很多遗产,还有很多垃圾和乱七八糟的文字。我希望检查所有这些文件以查找具有有效电子邮件ID的行,如果存在,则将其打印到文件中。请注意,我正在windows10上使用Cygwin(不确定这是否重要) 文本文件:Bash awk筛选具有有效电子邮件地址的行,bash,awk,Bash,Awk,我对bash和awk还不熟悉,我花了好几天的时间试图学习它。我认为我非常接近解决方案,但还没有完全解决。所以,请求你的帮助。请注意,我不希望使用grep,因为我发现它要慢得多 我有大量的文本文件,每个都有几百MB的大小。不幸的是,它们在任何一种格式中都没有完全标准化。加上这里有很多遗产,还有很多垃圾和乱七八糟的文字。我希望检查所有这些文件以查找具有有效电子邮件ID的行,如果存在,则将其打印到文件中。请注意,我正在windows10上使用Cygwin(不确定这是否重要) 文本文件: !bar@fo
!bar@foo.com,address
#john@foo.com;address
john@foo.com;address µÖ
email1@foo.com;username;address
email2@foo.com;username
email3@foo.com,username;address [spaces at the start of the row]
email4@foo.com|username|address [tabs at the start of the row]
awk -F'[,|;: \t]+' '{
gsub(/^[ \t]+|[ \t]+$/, "")
if (NF>1 && tolower($1) ~ /[0-9a-z_\-\.\+]+@[0-9a-z_\-\.]+\.[a-z0-9]+/)
{
r=gensub("[,|;: \t]+",":",1,$0)
print r > "file_good"
}
else
print $0 > "file_ignore"
}' *.txt
代码:
!bar@foo.com,address
#john@foo.com;address
john@foo.com;address µÖ
email1@foo.com;username;address
email2@foo.com;username
email3@foo.com,username;address [spaces at the start of the row]
email4@foo.com|username|address [tabs at the start of the row]
awk -F'[,|;: \t]+' '{
gsub(/^[ \t]+|[ \t]+$/, "")
if (NF>1 && tolower($1) ~ /[0-9a-z_\-\.\+]+@[0-9a-z_\-\.]+\.[a-z0-9]+/)
{
r=gensub("[,|;: \t]+",":",1,$0)
print r > "file_good"
}
else
print $0 > "file_ignore"
}' *.txt
预期输出到:文件\u良好
email1@foo.com:username;address
email2@foo.com:username
email3@foo.com:username;address
email4@foo.com:username|address
代码问题:
!bar@foo.com,address
#john@foo.com;address
john@foo.com;address µÖ
email1@foo.com;username;address
email2@foo.com;username
email3@foo.com,username;address [spaces at the start of the row]
email4@foo.com|username|address [tabs at the start of the row]
awk -F'[,|;: \t]+' '{
gsub(/^[ \t]+|[ \t]+$/, "")
if (NF>1 && tolower($1) ~ /[0-9a-z_\-\.\+]+@[0-9a-z_\-\.]+\.[a-z0-9]+/)
{
r=gensub("[,|;: \t]+",":",1,$0)
print r > "file_good"
}
else
print $0 > "file_ignore"
}' *.txt
任何帮助都将不胜感激 这不是一个完整的解决方案,但我可以想到一些初步步骤,这些步骤可能会使流程的其余部分变得更加简单
cat textfile | tr ';' '\n' | tr ',' '\n' | tr '\|' '\n' > textfile2
mv textfile2 textfile
sed -n '/\@/p' textfile > emails
sed -i '/\@/d' textfile
这将要做的是,尝试将所有这些分隔符转换成新行,这将产生将分隔字段放在单独行上的效果。在此之后,对包含“@”符号的所有行进行暴力搜索,希望至少能得到几个电子邮件地址,然后可以将这些地址转储到单独的文件中,并从原始文件中删除。从那里,如果你能找到一个共同的锚,你可能可以构建一个类似的启发式方法来提取用户名和snail地址
根据我的经验,正则表达式可以诱发字面意义上的偏头痛。在可能的情况下,我会尽量使用最简单的解决方案。如前所述,这很可能不是完美的;但这只是一个开始。虽然与所述目标相关的还有其他复杂性,但您最初的awk程序没有按预期工作的主要原因是正则表达式缺少锚定:
tolower($1) ~ /^[0-9a-z_\-\.\+]+@[0-9a-z_\-\.]+\.[a-z0-9]+$/
$1~/…/
更改为$1~/^…$/
。另外,原始程序的r=gensub
部分似乎没有做任何有用的事情(我在其他地方没有看到r
)gensub
特定于GNUawk
——在这种情况下,可能需要的只是sub
此注释可能会有所帮助:您的问题是,根据发布的示例输入显示预期的输出(因此,我们可以看到,例如,如果您希望打印一行或仅打印一行中的电子邮件地址。还可以在示例输入/输出中添加至少一种情况,即您在一行中有多个电子邮件地址,以及您的文本可能被误解为电子邮件地址但实际上无效。#john@foo.com
不是一封有效的电子邮件地址,但john@foo.com
就是这样,请在您的问题中解释工具应该如何知道#john@foo.com
是有效的电子邮件地址john@foo.com
之前正好有一个
而不是一个无效的电子邮件地址。您好,Ed。我使用的正则表达式应该能够过滤掉“#”或任何其他规范不知道错误在哪里:tolower($1)~/[0-9a-z\-\.\+]+@[0-9a-z\.-\.]+\.[a-z0-9]+/
它锚定为@user…除此之外,regexp还有一些问题。1)转义-
不可移植,只需将其放在括号表达式的开头或结尾。2)
和+
不是括号表达式中的元字符,因此它们不应转义。3) 使用小写字母的字符范围是不可移植的,您应该使用字符类。因此,你不应该把它写成/^[0-9a-z\-\\\\\.+][0-9a-z\\-\.+\.+\.[[a-z0-9]+$/
,你应该把它写成/^[:alnum:][:alnum:][-]+\.[:alnum:][-]+$/
,并且把改为tolower($1
,因为RE现在可以处理所有的情况。感谢用户13586221。。。我已经更正了上面的代码,以展示变量“r”的使用。我应该用gsub替换gensub吗?如果是,正确的代码是什么?@rogerwhite是的,可以与GNU awk一起使用。如果您不需要同时使用r
和原始$0
,r=gensub(“[,|::\t]+”,“:”,1)
可以替换为sub(“[,|:;:\t]+”,“:”
)。主要区别在于sub
和gsub
修改原始字符串并返回所做替换的数量,而gensub
返回修改后的字符串gensub
还具有其他功能,如backreferences&全局替换的第三个参数或第n个匹配。