一遍通过sed从文件中查找并打印两行行

时间:2019-06-03 04:27:04

标签: 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
...

我只知道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

编辑

块之间的换行当然不错,但是完全是可选的。

编辑2

请注意,INFOid: "value"不必在行的开头,在我的示例中,所有其他词都是任意的,并且事先不知道。我需要匹配的块之间和周围可以有任意数量的块(包括0)。

5 个答案:

答案 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 : elseConditional 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)

这是结合使用sedawk的替代解决方案。它使您可以按块或按记录分析输入。这种方法依赖于将awk记录分隔符(RS)设置为空字符串,这会使awk一次读取一个完整的块。

所以有2个步骤:

  1. 使输入可记录。
  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,上述行为都将相同。