使用 grep 或 awk 打印模式之间的文本块/行

使用 grep 或 awk 打印模式之间的文本块/行

我的文件file.txt如下所示:

[NamesA]
Andreas
Alex

[NamesB]
Bernd
Bruno

[NamesC]
Casper

[NamesD]
Doris

我想在 bash 脚本中使用grep或使用它来完成以下 3 个不同的输出:awk

  1. 输出

    [NamesB]   
    Bernd   
    Bruno
    
  2. 输出

    [NamesB]
    Bernd
    Bruno
    
    [NamesC] 
    Casper
    
  3. 输出

    [NamesD]
    Doris
    

我试过:

grep  -oP '\[NamesB\].*?' file.txt

但只获取[NamesB]而不是以下文本块。我设法得到了直接在后面但不在新行中的文本。

就是这样。如果我能得到至少所有以 开头的以下行[NamesB],但即使这样也不起作用。

  • 所以我可以想象 1. 的输出可能是最简单的,通过打印所有从[NamesB]下一个开始并结束的内容[
  • 我也可以想象这对于 2. 与输出 1. 类似,但随后运行grep2 次。一次与[NamesB]然后与[NamesC]

但是这对于 3. 来说如何工作,因为没有[下一个。并且可能有一个以 开头的未知的下一个块[

然后,该命令应开始打印以[NamesB]或 开头的文本,然后以下一个左括号[或文件末尾停止。

PS:我已经发布了一个类似的问题并找到了解决方案,但它是全部一行文本。在这个问题中,我有一个不同的情况,即文本块而不是单行。

答案1

您对选择要打印的记录的要求尚不清楚,但也许这就是您想要使用任何 awk 执行的操作:

输出 1,选项 1(一次读取 1 行):

$ awk '/^\[/{f=(/^\[NamesB]/)} f' file.txt
[NamesB]
Bernd
Bruno

输出1,选项2(一次读取1条多行记录)

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 == "[NamesB]"' file.txt
[NamesB]
Bernd
Bruno

输出 2,选项 1(打印 NamesB 记录及其后面的记录):

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 == "[NamesB]"{c=2} c&&c--' file.txt
[NamesB]
Bernd
Bruno

[NamesC]
Casper

输出 2,选项 2(打印 NamesB 和 NamesC 记录,无论它们位于输入中的何处):

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 ~ /^\[Names[BC]]$/' file.txt
[NamesB]
Bernd
Bruno

[NamesC]
Casper

输出 3,选项 1(打印 NamesD 记录):

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 == "[NamesD]"' file.txt
[NamesD]
Doris

输出 3,选项 2(打印输入中的第四条记录,无论它的名称是什么):

$ awk -v RS= -v ORS='\n\n' -F'\n' 'NR == 4' file.txt
[NamesD]
Doris

另外,关于:

如果我至少能得到以下所有以 [NamesB] 开头的行

以下内容可以做到这一点:

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 == "[NamesB]"{f=1} f' file.txt
[NamesB]
Bernd
Bruno

[NamesC]
Casper

[NamesD]
Doris

当然,可以编写许多其他脚本来根据各种标准生成输出,正确的脚本将取决于您选择要输出的块的要求。

答案2

使用 Raku(以前称为 Perl_6)

~$ raku -e 'for slurp.split("\n\n") { .put if / \[ NamesA \]  /};'   file

#OR

~$ raku -e '.put if / \[ NamesA \]  / for slurp.split("\n\n");'   file

以上是用 Raku(Perl 编程语言家族的成员)编写的答案。简而言之,文件被slurp一次性全部写入内存,并且split\n\n两个连续的换行符。结果元素(记录)通过使用进行迭代forif找到所需正则表达式的匹配项,元素(记录)就出来了put

输入示例:

[NamesA]
Andreas
Alex

[NamesB]
Bernd
Bruno

[NamesC]
Casper

[NamesD]
Doris

示例输出:

[NamesA]
Andreas
Alex

|请注意,您可以通过在正则表达式匹配器中使用 OR 符号返回多个记录。为了正确分隔返回,可以重写$_.putor部分,用尾随换行符填充每个记录:.putput "$_\n"

~$ raku -e 'put "$_\n" if / \[ NamesA | NamesB \]  / for slurp.split("\n\n");'   file
[NamesA]
Andreas
Alex

[NamesB]
Bernd
Bruno

注意:正则表达式匹配器可以是记录中的任何行。要专门匹配第一行,请使用/^ \[ NamesA \] $$ /,其中^表示字符串开头,$$表示行尾。

https://docs.raku.org
https://raku.org

答案3

您可以使用以下命令提取任何块(例如 NamesA):

$ awk '/^\[NamesA/{p=1; print; next} /^\[/{p=0}; p>0{print}' input_file
[NamesA]
Andreas
Alex

作为块头的第一个字符的 [ 需要进行转义,如代码所示。

使用这一单行,您可以打印任意输出组合以满足您的需求。

相关内容