是否可以让 sed 在替换部分(正则表达式组)中执行一些逻辑

是否可以让 sed 在替换部分(正则表达式组)中执行一些逻辑

我有几个 sed 命令:提取相关信息

我的文件sample.log(格式为ncsa.log)如下所示:

2012_04_01_filename.log:29874:192.168.1.12 - - [16/Aug/2012:12:54:21 +0000] "GET /cxf/myservice01/v1/abc?anyparam=anything&anotherone=another HTTP/1.1" 200 3224 "-" "client name"
2012_04_01_filename.log:29874:192.168.1.12 - - [16/Aug/2012:12:54:25 +0000] "GET /cxf/myservice02/v1/XYZ?anyparam=anything&anotherone=another HTTP/1.1" 200 3224 "-" "client name"
2012_04_01_filename.log:29874:192.168.1.12 - - [16/Aug/2012:12:56:52 +0000] "GET /cxf/myservice01/v1/rsv/USER02?anyparam=anything&anotherone=another HTTP/1.1" 200 6456 "-" "client name"
2012_04_01_filename.log:29874:192.168.1.12 - - [16/Aug/2012:12:58:52 +0000] "GET /cxf/myservice01/v2/upr/USER01?anyparam=anything&anotherone=another HTTP/1.1" 200 2424 "-" "client name"
2012_04_01_filename.log:29874:192.168.1.12 - - [16/Aug/2012:12:59:11 +0000] "GET /cxf/myservice02/v1/xyz?anyparam=anything&anotherone=another HTTP/1.1" 200 233 "-" "client name"

这组管道 sed 正在提取我需要的 url 详细信息(第一个 sed:\1 = YYYY-MM-DD 中的日期,\2 = service0x,\3 = trigram,\4 = 可选实体 id,\5 = HTTP 响应代码, \6 = http 响应大小)

more sample.log | sed -r 's#^(...._.._..)_.*/cxf/(myservice.*)/v./(.{3})[/]*([a-Z0-9]*)?.*\sHTTP/1.1.\s(.{3})\s([0-9]*)\s.*#\1;\2;\L\3;\E\4;\5;\6#g'  | sed -r 's!(.*;.*;.{3};)[a-Z0-9]+(;.*;.*)!\1retrieve\2!g' | sed -r 's!(.*);;(.*)!\1;list;\2!g' > request-by-operation.txt

需要的结果如下:

2012_04_01;myservice01;abc;list;200;3224
2012_04_01;myservice02;xyz;list;200;3224
2012_04_01;myservice01;rsv;retrieve;200;6456
2012_04_01;myservice01;upr;retrieve;200;2424
2012_04_01;myservice02;xyz;list;200;233

除了使用其他两个 sed 管道(完成这项工作)之外,我没有找到另一种方法来转换listand操作。retrieve

我听说 sed 不支持替换部分(在特定组上)中的命令,#\1;\2;\L\3;\Eifnull(\4, "list", "retrieve");\5;\6#但我想知道是否仍然可以仅使用一个 sed 命令以另一种方式完成此操作。

答案1

sed无法调用替换部分中的命令,但它可以运行多个替换。在这种情况下,将所有替换都放在一个sed似乎是可行的:

sed -r 's#^(...._.._..)_.*/cxf/(myservice.*)/v./(.{3})[/]*([a-Z0-9]*)?.*\sHTTP/1.1.\s(.{3})\s([0-9]*)\s.*#\1;\2;\L\3;\E\4;\5;\6#g;
        s!(.*;.*;.{3};)[a-Z0-9]+(;.*;.*)!\1retrieve\2!g;
        s!(.*);;(.*)!\1;list;\2!g'

答案2

您可以选择想要的部分,但也可以删除不需要的部分:

sed '
    s|_[^_]* /[^/]*/|;|
    s|/[^/]*/\(...\)|;\L\1|
    s|?[^"]*" |;list;|
    s|/.*;|;retrieve;|
    s/ /;/
    s/ .*$//'

答案3

GNU sed 确实有一个s///e命令,但它发送的是所有的模式空间到 shell 进行评估:

$ echo "echo hello world" | sed 's/world/foo bar | rev/e'
rab oof olleh

所以“world”被“foo bar | rev”取代。模式空间现在是“echo hello foo bar | rev”。这被发送到 shell,输出被放置在模式空间中,然后隐式打印。

Perl 的e标志允许您只关注字符串的匹配部分。

https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-命令

e

该命令允许将 shell 命令的输入通过管道传送到模式空间。如果进行了替换,则执行在模式空间中找到的命令,并用其输出替换模式空间。尾随换行符被抑制;如果要执行的命令包含 null 字符,则结果未定义。这是一个 GNU sed 扩展。

相关内容