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

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

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

[CATEGORY_1]
A=1
B=2

[CATEGORY_2]
C=3
D=4

E=5

...

[CATEGORY_N]
Z=26

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

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

A=1
B=2

我尝试使用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

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

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

答案1

您可能会考虑使用tomlqTOML 包装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

然后

$ tomlq -t '.CATEGORY_1' file.toml
A = 1
B = 2

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

$ tomlq -t --arg section 'CATEGORY_1' '.[$section]' file.toml
A = 1
B = 2

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

$ tomlq -r --arg section 'CATEGORY_1' '.[$section] | to_entries[] | [ .key, .value ] | @tsv' file.toml
A       1
B       2

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


由于您最初询问 grep 解决方案,其中pcregrep

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

where(?s)进行.匹配\n,以便.*?匹配多行。你在 PCRE 模式下使用 GNU grep 使用以下-z标志来伪造它:

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

由于它具有固定长度,如果您喜欢对称性,您可以\[CATEGORY_1\]\n\K用后视来替换(?<=\[CATEGORY_1\]\n)以匹配前视。(?=\n+\[)

答案2

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

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

  • 您可以在命令行上将所需的类别名称指定为awk变量catname
  • 在程序内部,如果标志p设置为 1,它将打印当前行(参见这里关于它是如何工作的)。
  • 如果我们遇到“类别开始模式”(行以 开头[并以 结束]),我们将标志设置为 0,但如果该行与类别名称完全匹配,我们将标志设置为 1(在某种意义上:我们设置p为检查$0当前行是否等于 ) 中存储的字符串的结果catname

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

延伸目标

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

{p=($0==catname)}

{p=($0==catname); next}

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

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

答案3

如果我理解正确的话,你可以使用这个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

相关内容