从文本文件中提取从特定类别标题开始的文本到下一个类别标题

Edu*_*eia 3 grep text-processing pcre toml

我有一个以下格式的 TOML 文件(类别可以有任何名称,顺序编号只是一个例子,不能保证):

[CATEGORY_1]
A=1
B=2

[CATEGORY_2]
C=3
D=4

E=5

...

[CATEGORY_N]
Z=26
Run Code Online (Sandbox Code Playgroud)

我想要实现的是检索给定类别中的文本。

所以,如果我指定,比方说,[CATEGORY_1]我希望它给我输出:

A=1
B=2
Run Code Online (Sandbox Code Playgroud)

我尝试使用标志grep来完成此任务,z因此它可以将换行符解释为空字节字符并使用此正则表达式:

(^\[.*])             # Match the category 
  ((.*\n*)+?         # Match the category content in a non-greedy way
    (?=\[|$))        # Lookahead to the start of other category or end of line
Run Code Online (Sandbox Code Playgroud)

除非我删除^了表达式开头的 ,否则它不起作用。但是,如果我这样做,它会将松散的括号错误解释为一个类别。

有没有办法正确地做到这一点?如果没有grep,则使用其他工具,例如sedawk

ste*_*ver 9

您可以考虑使用tomlq,一个TOML包装器jqYQ项目,让您检索类别的内容name简单地使用jq语法.name

前任。给出:

$ cat file.toml 
[CATEGORY_1]
A=1
B=2

[CATEGORY_2]
C=3
D=4

E=5


[CATEGORY_N]
Z=26
Run Code Online (Sandbox Code Playgroud)

然后

$ tomlq -t '.CATEGORY_1' file.toml
A = 1
B = 2
Run Code Online (Sandbox Code Playgroud)

...并使用命令行上给出的部分名称:

$ cat file.toml 
[CATEGORY_1]
A=1
B=2

[CATEGORY_2]
C=3
D=4

E=5


[CATEGORY_N]
Z=26
Run Code Online (Sandbox Code Playgroud)

输出为 TOML 格式。你想要制表符分隔的输出:

$ tomlq -t '.CATEGORY_1' file.toml
A = 1
B = 2
Run Code Online (Sandbox Code Playgroud)

使用@csv代替@tsv获取 CSV 输出。


由于您最初询问了 grep 解决方案,因此pcregrep

$ pcregrep -Mo '(?s)\[CATEGORY_1\]\n\K.*?(?=\n+\[)' file.toml
A=1
B=2
Run Code Online (Sandbox Code Playgroud)

where (?s)make .match\n以便.*?跨多行匹配。您可以使用以下-z标志在 PCRE 模式下使用 GNU grep 伪造它:

$ grep -Pzo '(?s)\[CATEGORY_1\]\n\K.*?\n(?=\n+\[)' file.toml
A=1                                                                                                                                                                                          
B=2
Run Code Online (Sandbox Code Playgroud)

由于它具有固定的长度,你可以更换\[CATEGORY_1\]\n\K一个回顾后(?<=\[CATEGORY_1\]\n),以匹配超前(?=\n+\[),如果你喜欢的对称性。

  • @Educorreia 没问题 - 该网站旨在提供其他人也可能认为有用的答案 (3认同)

Adm*_*Bee 7

比 pure 稍微复杂一些sed,但可以进行更多微调:

$ awk -v catname="[CATEGORY_1]" '/^\[.*\]$/{p=($0==catname)} p' input.toml
[CATEGORY_1]
A=1
B=2

Run Code Online (Sandbox Code Playgroud)
  • 您可以在命令行上将所需的类别名称指定为awkvariable catname
  • 在程序内部,如果标志p设置为 1 ,它将打印当前行(请参阅此处了解其工作原理)。
  • 如果我们遇到一个“类别开始模式”(行以[与两端]),我们设置标志为0,但如果行的类别名称完全匹配,我们设置了标志1(中的感觉:我们设置p到检查$0当前行是否等于存储在catname) 中的字符串的结果。

这样,从类别标题开始到下一个类别标题的所有内容都将被打印。

延伸目标

如果要省略类别标题,可以更改

{p=($0==catname)}
Run Code Online (Sandbox Code Playgroud)

{p=($0==catname); next}
Run Code Online (Sandbox Code Playgroud)

这将在设置标志后立即跳过处理到下一行,从而绕过条件打印指令。

如果另外您想排除空行,p请将程序末尾的“看似杂散”更改为p&&NF,这仅在标志p非零且至少有一个“字段”(即非空白)时才为真文本)在当前行。


sch*_*ity 5

如果我理解正确,您可以使用以下sed命令:

# Choose the category until the next [ character
# and then delete any line starting with the [ character
$ sed -n '/^\[CATEGORY_2\]/,/^\[/p' file | sed '/^\[/d'
C=3
D=4

E=5
Run Code Online (Sandbox Code Playgroud)