使用sed一次查找并打印文件中的两个行块

使用sed一次查找并打印文件中的两个行块,sed,Sed,我正在尝试使用一个sed命令从一个文本文件中查找并打印两个不同行数的块,如下所示: ... INFO first block to match id: "value" ... last line of the first block INFO next irrelevant block id: "different value" ... INFO second block to match id: "value" ... last line of the second block ... se

我正在尝试使用一个sed命令从一个文本文件中查找并打印两个不同行数的块,如下所示:

...
INFO first block to match
id: "value"
...
last line of the first block
INFO next irrelevant block
id: "different value"
...
INFO second block to match
id: "value"
...
last line of the second block
...
sed '1!s/^INFO/\n&/' infile | awk '/id: "value"/' RS= ORS='\n\n'
我只知道id值,以及每个块都以一行“INFO”开头这一事实。我希望匹配第一行中的每个块,而不在输出中包括下一个块的第一行:

INFO first block to match
id: "value"
...
last line of the first block

INFO second block to match
id: "value"
...
last line of the second block
理想情况下,我更喜欢一次完成,而不是让文件从上到下扫描多次。目前我有这个(它只匹配第一个块,我需要两者):

sed-n-e”/INFO/{'$!'{n;/INFO.*id:\'value\'/{:l;p;n;/^[\\[]/bl;}}}文件.log

编辑 块之间的换行当然很好,但完全是可选的

编辑2
请注意,
INFO
id:“value”
不必在行首,我的示例中的所有其他单词都是任意的,事先不知道。可以有任意数量的块(包括0)在我需要匹配的对象之间和周围。

sed
功能强大、简洁且愚蠢。
awk
更聪明

awk '/^INFO/{f = /match/? 1: 0} f'
编辑:我知道您希望在每个“块”之间使用换行符;如果我找到更紧密的方式,将更新:

awk '/^INFO/{f = /match/? 1: 0; if(i++) $0 = RS $0} f'
  • /^INFO/{action}
    :仅在以“INFO”开头的行上执行
    {action}
  • 变量=if?then:else
    :(三元运算符)
  • if(i++)
    :第一次计算时,
    i
    将为零,因此表达式将为false。这将防止在第一个块处出现额外的换行

  • $0=RS$0
    :在
    $0
    (整个记录)前加一个记录分隔符(换行符)

  • f
    如果f大于零,则隐含
    {print$0}

这可能适合您(GNU-sed):

此解决方案将所需的块存储在保留空间中,并以计数器为前缀。存储所需的块数后,计数器将被删除,块将被打印,进程将退出


该解决方案(仅基于提供的输入)假定
id
(如果存在)始终遵循
INFO
行。

这里有一个使用
sed
awk
组合的替代解决方案。它允许您按块或按记录解析输入。此方法依赖于设置
awk
记录分隔符(
RS
)到使awk一次读取一个完整块的空字符串

因此,有两个步骤:

  • 使输入记录可解析
  • 处理每个记录
  • 对于您的示例,这可能类似于:

    ...
    INFO first block to match
    id: "value"
    ...
    last line of the first block
    INFO next irrelevant block
    id: "different value"
    ...
    INFO second block to match
    id: "value"
    ...
    last line of the second block
    ...
    
    sed '1!s/^INFO/\n&/' infile | awk '/id: "value"/' RS= ORS='\n\n'
    
    输出:

    INFO要匹配的第一个块
    id:“值”
    ...
    第一个街区的最后一行
    要匹配的第二个块
    id:“值”
    ...
    第二个街区的最后一行
    
    awk很适合这一点,如果您可以将RS设置为多字符表达式,这将是一个理想的选择(gnu-awk允许这一点,但是有perl时为什么还要使用gnu-awk呢?)


    基本上,这会将记录分隔符(
    $/
    )设置为字符串“INFO”(因此现在每个“记录”都是perl的一行)。如果记录与模式
    id:“value”
    匹配,则会在开始处打印“INFO”。(如果没有
    -n
    ,perl将在每条记录的末尾保留记录分隔符,这不是您想要的),您可以在记录之间获得额外的换行符。一些代码可能会将其长度减半,但我的perl有些生疏。请在注释中等待较短的版本。

    这可能是您想要的,也可能不是您想要的,这取决于您的真实数据:

    $ awk '/INFO/{info=$0; f=0} /id: "value"/{print info; f=1} f' file
    INFO first block to match
    id: "value"
    ...
    last line of the first block
    INFO second block to match
    id: "value"
    ...
    last line of the second block
    
    或者,如果您想对每个块做更多的处理,而不仅仅是边走边打印,那么这种方法的一些变体会更好:

    $ awk '
        /INFO/ { prt() }
        { block = block $0 ORS }
        END { prt() }
        function prt() {
            if (block ~ /id: "value"/) {
                printf "%s", block
            }
            block=""
        }
    ' file
    INFO first block to match
    id: "value"
    ...
    last line of the first block
    INFO second block to match
    id: "value"
    ...
    last line of the second block
    

    使用任何UNIX设备上任何shell中的任何awk,上述行为都是相同的。

    您的意思是只提取ID?请通过编辑问题提供所需输出的示例。对于此类任务,sed是一个更好的工具。使用sed进行简单替换,而不做其他任何操作。@oguz ismail用于此操作的awk命令是什么?类似于是
    awk'/^INFO/{p=0;a=$0;next}$0==“id:\”值“{print a;p=1}p”文件
    您的“编辑2”本质上是说,您提供的示例没有充分涵盖您的需求。请修复该示例,否则我们无法测试潜在的解决方案,以查看其是否有效。您在下面有多个答案,并且您在下面的评论中为您的示例的问题道歉再次-修复这个例子。最后,sed用于在单个字符串上执行s/old/new-这不是你正在做的,所以你不应该考虑使用sed。哇,真是个怪物!哦,天哪,我愚蠢地认为我对sed有点满意:d这几乎可以工作,除了它输出第二个块后面的行作为嗯。我想阻止它,特别是因为其中一些块后面有某种控制字符序列,可以清除我的终端。你介意修改它来解决这个问题,并且允许信息和id被放置在行上的任意位置吗?你的命令在块之间输出一个换行符,这很好,但并不严格必要时,我在编辑手机中的问题时意外地将其放入示例输出中,我的坏。@dols3m我已删除了块之间的空行,但对于EDIT2?这非常简洁。不幸的是,它在我的情况下不起作用。请参阅“EDIT2”我的问题中的部分。如果我的示例有误导性,很抱歉。整洁,但信息可能不在行的开头,我仍然希望该行从一开始就打印出来。您介意根据我在“编辑2”部分中提到的内容对其进行更新吗?@dols3m:以何种方式?我显然没有具有代表性的数据样本。请阅读并确认坚持