使用sed一次查找并打印文件中的两个行块
我正在尝试使用一个sed命令从一个文本文件中查找并打印两个不同行数的块,如下所示:使用sed一次查找并打印文件中的两个行块,sed,Sed,我正在尝试使用一个sed命令从一个文本文件中查找并打印两个不同行数的块,如下所示: ... INFO first block to match id: "value" ... last line of the first block INFO next irrelevant block id: "different value" ... INFO second block to match id: "value" ... last line of the second block ... se
...
INFO first block to match
id: "value"
...
last line of the first block
INFO next irrelevant block
id: "different value"
...
INFO second block to match
id: "value"
...
last line of the second block
...
sed '1!s/^INFO/\n&/' infile | awk '/id: "value"/' RS= ORS='\n\n'
我只知道id值,以及每个块都以一行“INFO”开头这一事实。我希望匹配第一行中的每个块,而不在输出中包括下一个块的第一行:
INFO first block to match
id: "value"
...
last line of the first block
INFO second block to match
id: "value"
...
last line of the second block
理想情况下,我更喜欢一次完成,而不是让文件从上到下扫描多次。目前我有这个(它只匹配第一个块,我需要两者):
sed-n-e”/INFO/{'$!'{n;/INFO.*id:\'value\'/{:l;p;n;/^[\\[]/bl;}}}文件.log
编辑
块之间的换行当然很好,但完全是可选的
编辑2
请注意,
INFO
和id:“value”
不必在行首,我的示例中的所有其他单词都是任意的,事先不知道。可以有任意数量的块(包括0)在我需要匹配的对象之间和周围。sed
功能强大、简洁且愚蠢。awk
更聪明
awk '/^INFO/{f = /match/? 1: 0} f'
编辑:我知道您希望在每个“块”之间使用换行符;如果我找到更紧密的方式,将更新:
awk '/^INFO/{f = /match/? 1: 0; if(i++) $0 = RS $0} f'
:仅在以“INFO”开头的行上执行/^INFO/{action}
{action}
:(三元运算符)变量=if?then:else
:第一次计算时,if(i++)
将为零,因此表达式将为false。这将防止在第一个块处出现额外的换行i
:在$0=RS$0
(整个记录)前加一个记录分隔符(换行符)$0
如果f大于零,则隐含f
{print$0}
该解决方案(仅基于提供的输入)假定
id
(如果存在)始终遵循INFO
行。这里有一个使用sed
和awk
组合的替代解决方案。它允许您按块或按记录解析输入。此方法依赖于设置awk
记录分隔符(RS
)到使awk一次读取一个完整块的空字符串
因此,有两个步骤:
...
INFO first block to match
id: "value"
...
last line of the first block
INFO next irrelevant block
id: "different value"
...
INFO second block to match
id: "value"
...
last line of the second block
...
sed '1!s/^INFO/\n&/' infile | awk '/id: "value"/' RS= ORS='\n\n'
输出:
INFO要匹配的第一个块
id:“值”
...
第一个街区的最后一行
要匹配的第二个块
id:“值”
...
第二个街区的最后一行
awk很适合这一点,如果您可以将RS设置为多字符表达式,这将是一个理想的选择(gnu-awk允许这一点,但是有perl时为什么还要使用gnu-awk呢?)
基本上,这会将记录分隔符($/
)设置为字符串“INFO”(因此现在每个“记录”都是perl的一行)。如果记录与模式id:“value”
匹配,则会在开始处打印“INFO”。(如果没有-n
,perl将在每条记录的末尾保留记录分隔符,这不是您想要的),您可以在记录之间获得额外的换行符。一些代码可能会将其长度减半,但我的perl有些生疏。请在注释中等待较短的版本。这可能是您想要的,也可能不是您想要的,这取决于您的真实数据:
$ awk '/INFO/{info=$0; f=0} /id: "value"/{print info; f=1} f' file
INFO first block to match
id: "value"
...
last line of the first block
INFO second block to match
id: "value"
...
last line of the second block
或者,如果您想对每个块做更多的处理,而不仅仅是边走边打印,那么这种方法的一些变体会更好:
$ awk '
/INFO/ { prt() }
{ block = block $0 ORS }
END { prt() }
function prt() {
if (block ~ /id: "value"/) {
printf "%s", block
}
block=""
}
' file
INFO first block to match
id: "value"
...
last line of the first block
INFO second block to match
id: "value"
...
last line of the second block
使用任何UNIX设备上任何shell中的任何awk,上述行为都是相同的。您的意思是只提取ID?请通过编辑问题提供所需输出的示例。对于此类任务,sed是一个更好的工具。使用sed进行简单替换,而不做其他任何操作。@oguz ismail用于此操作的awk命令是什么?类似于是awk'/^INFO/{p=0;a=$0;next}$0==“id:\”值“{print a;p=1}p”文件
您的“编辑2”本质上是说,您提供的示例没有充分涵盖您的需求。请修复该示例,否则我们无法测试潜在的解决方案,以查看其是否有效。您在下面有多个答案,并且您在下面的评论中为您的示例的问题道歉再次-修复这个例子。最后,sed用于在单个字符串上执行s/old/new-这不是你正在做的,所以你不应该考虑使用sed。哇,真是个怪物!哦,天哪,我愚蠢地认为我对sed有点满意:d这几乎可以工作,除了它输出第二个块后面的行作为嗯。我想阻止它,特别是因为其中一些块后面有某种控制字符序列,可以清除我的终端。你介意修改它来解决这个问题,并且允许信息和id被放置在行上的任意位置吗?你的命令在块之间输出一个换行符,这很好,但并不严格必要时,我在编辑手机中的问题时意外地将其放入示例输出中,我的坏。@dols3m我已删除了块之间的空行,但对于EDIT2?这非常简洁。不幸的是,它在我的情况下不起作用。请参阅“EDIT2”我的问题中的部分。如果我的示例有误导性,很抱歉。整洁,但信息可能不在行的开头,我仍然希望该行从一开始就打印出来。您介意根据我在“编辑2”部分中提到的内容对其进行更新吗?@dols3m:以何种方式?我显然没有具有代表性的数据样本。请阅读并确认坚持