我有一个以下格式的 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
,则使用其他工具,例如sed
或awk
。
答案1
您可能会考虑使用tomlq
TOML 包装jq
器yq项目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