从输入中提取感兴趣的子字符串(如下所示)的最快方法是什么?
MsgTrace(65/26)noop:user=xxx=INBOX:cmd=534
ImapFetchComplete(56/39)user=xxxxxxxxxx
所需的输出(即本例中字符串
:
后面以 MsgTrace(65/26)
结尾的字符串):
noop
我尝试了以下方法,但没有成功:
egrep -i "[a-zA-Z]+\(.*\)[a-z]+:"
grep
默认返回整行。
虽然选项
-o
将输出限制为仅正则表达式匹配的行的部分,但在本例中这仍然不够,因为您需要该匹配的 substring。
但是,由于您使用的是 Linux,您可以使用 GNU
grep
的 -P
选项(用于支持 PCREs,Perl 兼容的正则表达式),它允许通过以下功能提取 submatch作为 \K
(删除到目前为止匹配的所有内容)和 (?=...)
(对匹配没有贡献的前瞻断言):
$ grep -Po "[a-zA-Z]\(.*\)\K[a-z]+(?=:)" <<'EOF'
MsgTrace(65/26)noop:user=xxx=INBOX:cmd=534
ImapFetchComplete(56/39)user=xxxxxxxxxx
EOF
noop # output
Ed Morton 指出(在一条已删除的评论中)GNU
grep
的 man
页面仍然将 -P
选项称为“高度实验性”,可能“警告未实现的功能”,但该选项已已经存在多年了,实际上我还没有看到警告或性能问题 - YMMV。
在当前的情况下,上述命令甚至优于
sed
和 awk
解决方案 - 请参阅 NeronLeVelu 有用的性能比较。
这篇有趣的文章 Ed 指出讨论了可以 与正则表达式引擎一起出现的潜在性能问题,例如
grep -P
(通过 PCRE 库)、Perl 本身以及许多其他广泛使用(且成熟)的正则表达式引擎所使用的,例如在 Python、Ruby 和 PHP 中:
a?a?a?a?aaaa
来匹配 aaaa
。你可以试试这个:
$ sed -n 's/[[:alpha:]]*([^)]*)\([[:lower:]]*\):.*/\1/p' file
noop
它可移植到所有 POSIX sed,并且不使用 PCRE,仅使用 BRE,因此正则表达式匹配部分至少应该很快。
对此类示例条目的 2469120 行文本进行快速而肮脏的测试,将
grep -PO
作为获胜者
time sed -n -e 's/^MsgTrace[^)]\{4,\})//;t M' -e 'b' -e ':M' -e 's/:.*//p' YourFile >/dev/null
real 0m7.61s
user 0m7:10s
sys 0m0.13s
time awk -F ':' '/^MsgTrace/{ sub( /.*)/, "", $1); print $1}' YourFile >/dev/null
real 0m17.43s
user 0m16.19s
sys 0m0.17s
time grep -Po "[a-zA-Z]\(.*\)\K[a-z]+(?=:)" YourFile >/dev/null
real 0m6.72s
user 0m6.23s
sys 0m0.11s
time sed -n 's/[[:alpha:]]*([^)]*)\([[:lower:]]*\):.*/\1/p' YourFile >/dev/null
real 0m17.43s
user 0m16.29s
sys 0m0.12s
time grep -Po '(?<=MsgTrace\(65/26\)).*?(?=:)' YourFile >/dev/null
real 0m16.38s
user 0m15.22s
sys 0m0.15s
对于@EdMorton 问题(我重做相同的原始 sed 以在相同的机器负载上下文中具有比较值)。确切的字符串要快得多,我想 sed 在选择所有标准中最长的一个之前会尝试几种组合,其中
.*l
比 pool is full
提供更多的可能性
time sed -n -e 's/^MsgTrace([^)]\{3,\})//;T' -e 's/:.*//p' YourFile >/dev/null
real 0m7.28s
user 0m6.60s
sys 0m0.13s
time sed -n -e 's/^[[:alpha:]]*([^)]\{3,\})//;T' -e 's/:.*//p' YourFile >/dev/null
real 0m10.44s
user 0m9.67s
sys 0m0.14s
time sed -n -e 's/^[[:alpha:]]*([^)]*)//;T' -e 's/:.*//p' YourFile >/dev/null
real 0m10.54s
user 0m9.75s
sys 0m0.11s