我的文件file.txt
如下所示:
[NamesA]
Andreas
Alex
[NamesB]
Bernd
Bruno
[NamesC]
Casper
[NamesD]
Doris
我想在 bash 脚本中使用grep
或使用它来完成以下 3 个不同的输出:awk
输出
[NamesB] Bernd Bruno
输出
[NamesB] Bernd Bruno [NamesC] Casper
输出
[NamesD] Doris
我试过:
grep -oP '\[NamesB\].*?' file.txt
但只获取[NamesB]
而不是以下文本块。我设法得到了直接在后面但不在新行中的文本。
就是这样。如果我能得到至少所有以 开头的以下行[NamesB]
,但即使这样也不起作用。
- 所以我可以想象 1. 的输出可能是最简单的,通过打印所有从
[NamesB]
下一个开始并结束的内容[
。 - 我也可以想象这对于 2. 与输出 1. 类似,但随后运行
grep
2 次。一次与[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
两个连续的换行符。结果元素(记录)通过使用进行迭代for
:if
找到所需正则表达式的匹配项,元素(记录)就出来了put
。
输入示例:
[NamesA]
Andreas
Alex
[NamesB]
Bernd
Bruno
[NamesC]
Casper
[NamesD]
Doris
示例输出:
[NamesA]
Andreas
Alex
|
请注意,您可以通过在正则表达式匹配器中使用 OR 符号返回多个记录。为了正确分隔返回,可以重写$_.put
or部分,用尾随换行符填充每个记录:.put
put "$_\n"
~$ raku -e 'put "$_\n" if / \[ NamesA | NamesB \] / for slurp.split("\n\n");' file
[NamesA]
Andreas
Alex
[NamesB]
Bernd
Bruno
注意:正则表达式匹配器可以是记录中的任何行。要专门匹配第一行,请使用/^ \[ NamesA \] $$ /
,其中^
表示字符串开头,$$
表示行尾。
答案3
您可以使用以下命令提取任何块(例如 NamesA):
$ awk '/^\[NamesA/{p=1; print; next} /^\[/{p=0}; p>0{print}' input_file
[NamesA]
Andreas
Alex
作为块头的第一个字符的 [ 需要进行转义,如代码所示。
使用这一单行,您可以打印任意输出组合以满足您的需求。