正则表达式在终端与在线验证工具行为差异及日志提取适配问题
问题描述
我编写了一个正则表达式用于从模板化的Envoy日志字符串中提取值,该正则在regexr.com等在线验证工具中运行正常,但在Shell环境中执行时出现异常。
日志示例
[2022-11-11T12:07:00.789Z] "GET /check?subject=johnbegucci HTTP/1.1" 200 - "-" 0 17 3 2 "-" "-" "4e4c4fb1-a4d8-4075-8e42-b5fb9216f863" "laundry.transaction.svc.cluster.local:4466" "172.16.107.246:4466" outbound|4466||laundry.transaction.svc.cluster.local 172.16.67.246:51630 10.100.111.246:4466 172.16.67.246:48610 - default
[2022-11-11T13:31:41.189Z] "GET /v1/campaign/198237-jsd-1231 HTTP/1.1" 200 - "-" 0 674 63 63 "-" "Apache-HttpClient/4.5.10 (Java/11.0.7)" "9b3afd5b-c092-4e84-9f29-6380b7f2cafc" "mkt-extractor.mkt-extractor" "172.16.108.138:80" outbound|80||mkt-extractor.mkt-extractor.svc.cluster.local 172.16.65.24:57134 10.100.19.249:80 172.16.65.24:38816 - default
日志格式模板
[%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" %UPSTREAM_CLUSTER% %UPSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_REMOTE_ADDRESS% %REQUESTED_SERVER_NAME%\n
现有正则与问题
我编写的正则用于提取UPSTREAM_HOST的值(如outbound|4466||laundry.transaction.svc.cluster.local):
(\[.*\])\s(\".*\")\s([0-9]*)\s(.*)\s(\".*\")\s([0-9]*)\s([0-9]*)\s([0-9]*)\s([0-9]*)\s(\".*\")\s(\".*\")\s(\".*\")\s(\".*\")\s(\".*\")\s(.*)\s(.*)\s(.*)\s(.*)\s(.*)\s(.*)+
在regexr.com上测试时,该正则能正确将两行日志的UPSTREAM_HOST匹配为第14组,但使用awk -v FPAT执行时分组结果异常。提取UPSTREAM_HOST时不同日志需用不同print位置,不利于自动化处理,请问如何用同一print位置处理所有此类日志?
解决方案
方法1:用awk的FPAT正确分割字段
Envoy日志存在带空格的引号字段,直接按空格分割会错位。用FPAT定义字段规则,优先匹配引号包裹的内容,再匹配无空格的连续字符,确保每个字段分割准确:
awk -v FPAT='("[^"]+"|[^[:space:]]+)' '{print $14}' 你的日志文件名
这样UPSTREAM_HOST固定是第14个字段,无论日志内容如何变化,直接print $14即可提取。
方法2:用sed精准捕获目标字段
通过匹配UPSTREAM_HOST之前的所有固定格式内容,捕获目标字段并输出:
sed -E 's/^(\[[^]]+\] "[^"]+" [0-9]+ [^ ]+ "[^"]+" [0-9]+ [0-9]+ [0-9]+ [0-9]+ "[^"]+" "[^"]+" "[^"]+" "[^"]+" "[^"]+") ([^ ]+) .*/\2/' 你的日志文件名
该命令将整个行替换为捕获到的UPSTREAM_HOST值,直接输出结果。
方法3:用grep的PCRE模式提取
如果你的grep支持-P参数(PCRE正则),可以用正向预查定位目标字段:
grep -Po '(?<=^\[[^]]+\] "[^"]+" [0-9]+ [^ ]+ "[^"]+" [0-9]+ [0-9]+ [0-9]+ [0-9]+ "[^"]+" "[^"]+" "[^"]+" "[^"]+" "[^"]+" )[^ ]+' 你的日志文件名
正向预查匹配UPSTREAM_HOST前的所有内容,然后提取后续的非空格字符(即目标值)。
内容的提问来源于stack exchange,提问作者placplacboom

