【问题标题】:Convert regex from Python format to GNU Sed format将正则表达式从 Python 格式转换为 GNU Sed 格式
【发布时间】:2014-11-13 06:35:26
【问题描述】:

我正在解析一个大约 10GB 的日志文件,并且需要通过 sed 将其提供给它以捕获一些输出。基于我将在 JavaScript 中使用的必要捕获段是:

s/method=""([^"]*)"".*path=""([^"]*)"".*accept=""([^"]*)""/"\1","\2","\3"/

不幸的是 sed(GNU sed 4.2.1,GnuWin32 版)在[^"]* 范围内苦苦挣扎。它拒绝匹配它们。我已经尝试过其他接受块的变体,[a-zA-Z0-9:\\/.]* 和类似的变体,但似乎总是在它错过的块内有新字符,实际上我可以接受引号之间的任何有效字符。由于 sed 的 * 例程是一个贪婪的实现,它往往在最后的“接受”项上也有问题,将日志条目中的所有其他项拉到最后。

我需要捕获引号之间的所有内容并忽略日志条目的其余部分。

我已经在这两天做了一些愚蠢的事情,如果不需要从带有 sed 的脚本执行的话,我可以直接在 python 中实现。任何正则表达式专家可以提供帮助吗?

编辑:

有关示例的额外信息,这在我的系统上没有匹配项,来自 GnuWin32.sourceforge.net 集合的 sed 4.2.1:sed -r 's/method=""([^"]*)"".*path=""([^"]*)"".*accept=""([^"]*)""/"\1","\2","\3"/' logfile

这会为某些条目生成匹配项:sed -r 's/^.*\method\=""([A-Z]*).*path=""([a-zA-Z0-9:\/]*).*accept=""(.*)"".*/"\1","\2","\3"/ logfile

以下是一些(略有删节但不是太多)行:

"server-01/1.2.3.4    time=""Wed Oct 29 05:59:59 GMT+00:00 2014"" method=""GET"" path=""/ourapp/foo/bar/AAA-123:1029"" status=""200"" message=""OK"" duration=""7"" query=""cc=1463648"" content_type=""application/json"" referer=""https://example.org/somewhere"" from=""foo@bar.com"" ip=""1.2.3.4"" agent=""Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36"" req_header_accept=""application/json, text/javascript, application/sord+xml; q=0.01"" req_header_accept-language=""en-US,en;q=0.8"" req_header_x-request-id=""29/Oct/2014:05:59:59.968a-abc123ABC"" req_header_x-forward=""1.2.3.4"" req_header_x-forwarded-for=""1.2.3.4"" ","2014-10-28T23:59:59.000-0000","someapp-01.a",production,1,"/home/someapp/log/ourapp-access.log","ut01-splunkidx18.i"

"server-01/1.2.3.4    time=""Wed Oct 29 05:59:59 GMT+00:00 2014"" method=""GET"" path=""/ourapp/foo/bar:AA9.1/ABC-123/record"" status=""200"" message=""OK"" duration=""73"" query=""view=includeFields"" content_type=""application/json"" from=""None"" ip=""1.2.3.4"" req_header_accept=""application/json"" req_header_x-request-id=""ab123-abc123-12345abc"" req_header_x-forward=""1.2.3.4"" req_header_x-forwarded-for=""1.2.3.4"" ","2014-10-28T23:59:59.000-0000","someapp-01.a",production,1,"/home/someapp/log/ourapp-access.log","ut01-splunkidx18.i"

"server-01/1.2.3.4    time=""Wed Oct 29 05:59:59 GMT+00:00 2014"" method=""HEAD"" path=""/ourapp/foo/bar:AA3.4/ABC-123/meta"" status=""200"" message=""OK"" duration=""21"" content_type=""application/json"" from=""foo@bar.com"" ip=""1.2.3.4"" agent=""Java/1.7.0_25"" req_header_accept=""application/json"" req_header_accept-language=""en"" req_header_cache-control=""no-cache"" req_header_x-request-id=""29/Oct/2014:05:59:59.882va-af527A"" req_header_x-forward=""1.2.3.4"" req_header_x-forwarded-for=""1.2.3.4"" ","2014-10-28T23:59:59.000-0000","someapp-01.a",production,1,"/home/someapp/log/ourapp-access.log","ut01-splunkidx18.i"

【问题讨论】:

  • [^"]* 在 GNU sed 中运行良好。请显示您用来调用sed完成 命令。 (为了获得最佳结果,还请显示一些小样本输入和相应的所需输出。)
  • 信息已添加到原始帖子中,无法评论。

标签: regex sed


【解决方案1】:

这个问题的关键在于 Windows shell 与sed 命令的交互。有关详细信息,请参阅此答案的最后一部分。

Unix shell 下的演示

作为样本输入考虑:

$ cat file
some method=""this is my method"" more stuff path=""My Path""  accept=""Yes"" end of line

以下sed 命令处理该输入:

$ sed -r 's/.*method=""([^"]*)"".*path=""([^"]*)"".*accept=""([^"]*)"".*/"\1","\2","\3"/' file
"this is my method","My Path","Yes"

请注意,-r 选项是必需的,以便未转义的括号充当分组而不是文字字符。

在修改后的问题中使用更复杂的输入:

$ sed -r 's/.*method=""([^"]*)"".*path=""([^"]*)"".*accept=""([^"]*)"".*/"\1","\2","\3"/' input
"GET","/ourapp/foo/bar/AAA-123:1029","application/json, text/javascript, application/sord+xml; q=0.01"

"GET","/ourapp/foo/bar:/AA9.1/ABC-123/record","application/json"

"HEAD","/ourapp/foo/bar:/AA3.4/ABC-123/meta","application/json"

关于accept 问题,我在示例输入中看到两个accept 变量:

req_header_accept
req_header_accept-language

因为正则表达式匹配accept="",所以应该匹配前者,而不是后者。

匹配非引号

考虑输入:

$ cat test.txt
Billy "The Kid" Smith
Jimmy "The Fish" Stuart
Chuck "The Man" Norris

sed 命令选择引用的材料:

$ sed -r 's/.*"([^"]*)".*/\1/' test.txt
The Kid
The Fish
The Man

所有这些测试都是在 linux 下的 GNU sed 版本 4.2.1 上完成的。

Windows Shell 问题

以下是使sed 命令在Windows 上工作的关键点:

  • 用双引号将sed 命令括起来。在 Windows shell 下,命令应该用双引号保护,而不是 Unix 使用的单引号。

  • 如果字符串需要包含双引号,请将它们以十六进制编码写成\x22

  • 在 Windows 下,不带引号的插入符号 ^ 是转义字符。但是,这不会影响我们,因为在我们的例子中,^ 总是出现在双引号字符串中。

  • CygWin(如果可用)可避免 Windows shell 问题。

因此,对于 Billy The Kid 输入,请尝试:

sed -r "s/.*\x22([^\x22]*)\x22.*/\1/" test.txt

另外,^ 是一个 Windows 转义字符,但据报道它只能用作外部引号。因此,我将其保留在上述命令中。

对于完整的案例,Bryan 报告说以下方法有效:

sed -r "s/^.*method\=\x22\x22([^\x22]*).*path=\x22\x22([^\x22]*).*req_header_accept=\x‌​22\x22([^\x22]*).*$/\x22\1\x22,\x22\2\x22,\x22\3\x22/" logfile

【讨论】:

  • 谢谢。我知道-r。不幸的是,似乎 sed 令人窒息的是[^"]*。我花了几个小时试图追踪这个问题,但找不到解决方案。出于某种原因,它绝对拒绝使用“非引用”集。三个测试行:Billy "The Kid" Smith Jimmy "The Fish" Stuart Chuck "The Man" Norris 这行得通,提取引号内的内容:sed -r 's/.*"([a-zA-Z ]*)".*/\1/' test.txt 但这没有结果:sed -r 's/.*"([^"]*)".*/\1/' test.txt
  • 您的 sed 命令为孩子输入的账单对我有用(在更新的答案中输出)。我在 linux 上使用 GNU sed 但[^"]* 真的应该在任何sed 中工作,无论是否使用 GNU。我对 Windows 不熟悉。可以与 Windows shell 进行一些交互吗?
  • @Bryan 根据this answer,存在Windows shell 问题。其一," 扮演' 的角色,其二,^ 是 Windows 转义字符。
  • 谢谢,这个链接足以让这个愚蠢的东西正常工作。
  • 哇,这是对答案的修改。我会将大量重写的答案标记为有用,但您提供的解锁它的是 this link,而不是您在答案中提供的重写。
猜你喜欢
  • 2012-01-12
  • 1970-01-01
  • 1970-01-01
  • 2012-03-27
  • 2015-01-15
  • 2021-10-22
  • 2014-11-08
  • 1970-01-01
  • 2018-10-08
相关资源
最近更新 更多