我试图提出一个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
...
我只知道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;}}}" file.log
块之间的换行当然不错,但是完全是可选的。
请注意,INFO
和id: "value"
不必在行的开头,在我的示例中,所有其他词都是任意的,并且事先不知道。我需要匹配的块之间和周围可以有任意数量的块(包括0)。
答案 0 :(得分:1)
sed
功能强大,简洁明了。 awk
更聪明!
awk '/^INFO/{f = /match/? 1: 0} f'
编辑:我看到你想在每个“块”之间换行;如果我发现更严格的方法,它将更新:
awk '/^INFO/{f = /match/? 1: 0; if(i++) $0 = RS $0} f'
/^INFO/{action}
:仅在以“ INFO”开头的行上执行{action}
variable = if ? then : else
:Conditional Expression(三元运算符) if(i++)
:第一次求值时,i
为零,因此表达式为false。这样可以防止在第一个块处出现额外的换行符。
$0 = RS $0
:在$0
(整个记录)前添加一个记录分隔符(换行符)
f
如果f大于零,则暗示{print $0}
。
答案 1 :(得分:1)
这可能对您有用(GNU sed):
id
此解决方案将所需的块存储在保留空间中,并以计数器为前缀。一旦存储了所需数量的块,便会删除计数器,然后打印块并退出该过程。
该解决方案(仅基于提供的输入)假定INFO
(如果存在)始终遵循{
"valido": true,
"nombre": "Anderson",
"apellido": "Laverde Gracia",
"email": "ander.laverde.dev@gmail.com",
"periodo": "0985",
"emplid": "8923082",
"x-t6519fdd1s5q": "eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE1NTk3MTQ1MTUsInN1YiI6ImFuZGVyc29ubGF2ZXJkZTE2In0.T8his-2cpGgFwkxmDGOeZ2rAGKIrSyrgPvfUs2xZUkiD6Z4hi7Dhg-p28y8dKBr3TW_97kqnSEY1Pg-k5AYuAA",
"token_expire_in": 1559714515418,
"roles": [
"3"
]
}
行。
答案 2 :(得分:0)
这是结合使用sed
和awk
的替代解决方案。它使您可以按块或按记录分析输入。这种方法依赖于将awk
记录分隔符(RS
)设置为空字符串,这会使awk一次读取一个完整的块。
所以有2个步骤:
对于您的示例,这可能是这样的:
sed '1!s/^INFO/\n&/' infile | awk '/id: "value"/' RS= ORS='\n\n'
输出:
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
答案 3 :(得分:0)
awk对此很有用,如果您可以将RS设置为多字符表达式,那将是理想的选择。 (gnu awk允许这样做,但是当有perl时为什么还要打扰gnu awk?)
perl -wnle 'BEGIN{$/="INFO"; undef $\} print "$/$_" if m/id: \"value\"/' input
基本上,这会将记录分隔符($/
)设置为字符串“ INFO”(因此,每个“记录”都是perl的“行”)。如果记录与模式id: "value"
相匹配,则在其开头打印“ INFO”。 (没有-n
,perl将在每个记录的末尾保留记录分隔符,这不是您想要的)。通过省略“ undef $ \”,您可以在记录之间获得额外的换行符。一些代码高尔夫可能会将其长度减少一半,但是我的perl有点生锈。等待评论中的简短版本。
答案 4 :(得分:0)
根据实际数据的样子,这可能是您想要的,也可能不是:
$ 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,上述行为都将相同。