Awk 如何在多行模式之间提取线?

Awk 如何在多行模式之间提取线?,awk,sed,Awk,Sed,我有一个文件,看起来像: blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah <empty line here> Total DOS and NOS and partial (IT) DOSDOWN <empty line here> E

我有一个文件,看起来像:

  blah blah blah blah blah blah blah blah 
  blah blah blah blah blah blah blah blah 
  blah blah blah blah blah blah blah blah 
<empty line here>
     Total DOS and NOS and partial (IT) DOSDOWN   
<empty line here>
     E     Total     1
<empty line here>
-1.5000    0.004    0.000    0.004
-1.4953    0.004    0.000    0.004
-1.4906    0.004    0.000    0.004
-1.4859    0.004    0.000    0.004
-1.4812    0.004    0.000    0.004
 0.3563    0.708    5.510    0.708
 0.3609    0.562    5.513    0.562
 0.3656    0.381    5.515    0.381
 0.3703    0.149    5.517    0.149
<empty line here>
     Sublattice  1 Atom Fe   spin DOWN   
因此,在一天结束时,我希望在两个多行模式之间有一条线。 据我所知,
awk
可以通过状态机检测多行模式(请参阅),但在我的例子中,我没有做到这一点


任何关于如何解决此问题的建议都将不胜感激。

使用
sed
sed-n'5,/^$/{/^$/d}'

但这假定“多行起始模式”始终位于文件的开头。否则它会变得更复杂。像这样:

/Total/{N;N;N}
/Total.*Total/,/^$/{
    /Total/d
    /^$/d
}

这里我假设“Total”匹配多行模式的开头,“Total.*Total”匹配整个模式。替换
N;N如果有其他模式以多行模式的第一行开始,但短于4行,则使用更复杂的代码。

使用
sed
sed-N'5,/^$/{^$/d}'

但这假定“多行起始模式”始终位于文件的开头。否则它会变得更复杂。像这样:

/Total/{N;N;N}
/Total.*Total/,/^$/{
    /Total/d
    /^$/d
}

这里我假设“Total”匹配多行模式的开头,“Total.*Total”匹配整个模式。替换
N;NN
如果有其他模式以多行模式的第一行开始,但短于4行,则更复杂。

从您的评论中,您需要的似乎是:

awk -v RS= '/Total DOS/{tgt=NR+2} NR==tgt' file
如果没有,则编辑您的问题以澄清。如果您只需要文件输出中的第一个匹配块,并且效率是一个问题,请将其设置为
NR==tgt{print;exit}
。如有必要,将regexp更改为您需要匹配的
总DOS…
行中的最大值,以使其唯一

在这里,它是针对您提供的示例输入运行的:

$ cat file
  blah blah blah blah blah blah blah blah
  blah blah blah blah blah blah blah blah
  blah blah blah blah blah blah blah blah

     Total DOS and NOS and partial (IT) DOSUP

     E     Total     1

  -1.5000    0.004    0.000    0.004
  -1.4953    0.004    0.000    0.004
  -1.4906    0.004    0.000    0.004
  .......    .....    .....    .....
   0.3609    0.562    5.513    0.562
   0.3656    0.381    5.515    0.381
   0.3703    0.149    5.517    0.149

   blah      blah     blah     blah

$ awk -v RS= '/Total DOS/{tgt=NR+2} NR==tgt' file
  -1.5000    0.004    0.000    0.004
  -1.4953    0.004    0.000    0.004
  -1.4906    0.004    0.000    0.004
  .......    .....    .....    .....
   0.3609    0.562    5.513    0.562
   0.3656    0.381    5.515    0.381
   0.3703    0.149    5.517    0.149

从您的评论来看,您所需要的似乎是:

awk -v RS= '/Total DOS/{tgt=NR+2} NR==tgt' file
如果没有,则编辑您的问题以澄清。如果您只需要文件输出中的第一个匹配块,并且效率是一个问题,请将其设置为
NR==tgt{print;exit}
。如有必要,将regexp更改为您需要匹配的
总DOS…
行中的最大值,以使其唯一

在这里,它是针对您提供的示例输入运行的:

$ cat file
  blah blah blah blah blah blah blah blah
  blah blah blah blah blah blah blah blah
  blah blah blah blah blah blah blah blah

     Total DOS and NOS and partial (IT) DOSUP

     E     Total     1

  -1.5000    0.004    0.000    0.004
  -1.4953    0.004    0.000    0.004
  -1.4906    0.004    0.000    0.004
  .......    .....    .....    .....
   0.3609    0.562    5.513    0.562
   0.3656    0.381    5.515    0.381
   0.3703    0.149    5.517    0.149

   blah      blah     blah     blah

$ awk -v RS= '/Total DOS/{tgt=NR+2} NR==tgt' file
  -1.5000    0.004    0.000    0.004
  -1.4953    0.004    0.000    0.004
  -1.4906    0.004    0.000    0.004
  .......    .....    .....    .....
   0.3609    0.562    5.513    0.562
   0.3656    0.381    5.515    0.381
   0.3703    0.149    5.517    0.149

这是一个基于Ed Morton技巧的解决方案

awk -v RS= 'n==2; /Total DOS/ || n {n++;next} {n=0}' input.txt
这就是它的工作原理

  • RS=
    将awk置于多行模式,以便记录包含行块
  • n==2打印满足此条件时处理的任何记录
  • /RE/| | n
    是一个条件,如果RE(模式)在当前记录中匹配,或者变量
    n
    非零,则该条件的计算结果为true
  • {n++;next}
    明显增加
    n
    并跳到下一条记录
  • {n=0}
    如果我们还没有跳到下一条记录,我们将重置
    n
所有这一切的效果是,我们打印的记录是两条记录,在一条记录之后是一条具有匹配模式的记录。当然,您可以将计数器开始的条件调整为您喜欢的任何条件<例如,代码>$2==“总计”
。根据口味加盐

sh-3.2$ cat input.txt
  blah blah blah blah blah blah blah blah
  blah blah blah blah blah blah blah blah
  blah blah blah blah blah blah blah blah

     Total DOS and NOS and partial (IT) DOSUP

     E     Total     1

  -1.5000    0.004    0.000    0.004
  -1.4953    0.004    0.000    0.004
  -1.4906    0.004    0.000    0.004
  .......    .....    .....    .....
   0.3609    0.562    5.513    0.562
   0.3656    0.381    5.515    0.381
   0.3703    0.149    5.517    0.149

   blah      blah     blah     blah

sh-3.2$ awk -v RS=  'n==2; /Total DOS and NOS/||n{n++;next} {n=0}' input.txt
  -1.5000    0.004    0.000    0.004
  -1.4953    0.004    0.000    0.004
  -1.4906    0.004    0.000    0.004
  .......    .....    .....    .....
   0.3609    0.562    5.513    0.562
   0.3656    0.381    5.515    0.381
   0.3703    0.149    5.517    0.149

这是一个基于Ed Morton技巧的解决方案

awk -v RS= 'n==2; /Total DOS/ || n {n++;next} {n=0}' input.txt
这就是它的工作原理

  • RS=
    将awk置于多行模式,以便记录包含行块
  • n==2打印满足此条件时处理的任何记录
  • /RE/| | n
    是一个条件,如果RE(模式)在当前记录中匹配,或者变量
    n
    非零,则该条件的计算结果为true
  • {n++;next}
    明显增加
    n
    并跳到下一条记录
  • {n=0}
    如果我们还没有跳到下一条记录,我们将重置
    n
所有这一切的效果是,我们打印的记录是两条记录,在一条记录之后是一条具有匹配模式的记录。当然,您可以将计数器开始的条件调整为您喜欢的任何条件<例如,代码>$2==“总计”。根据口味加盐

sh-3.2$ cat input.txt
  blah blah blah blah blah blah blah blah
  blah blah blah blah blah blah blah blah
  blah blah blah blah blah blah blah blah

     Total DOS and NOS and partial (IT) DOSUP

     E     Total     1

  -1.5000    0.004    0.000    0.004
  -1.4953    0.004    0.000    0.004
  -1.4906    0.004    0.000    0.004
  .......    .....    .....    .....
   0.3609    0.562    5.513    0.562
   0.3656    0.381    5.515    0.381
   0.3703    0.149    5.517    0.149

   blah      blah     blah     blah

sh-3.2$ awk -v RS=  'n==2; /Total DOS and NOS/||n{n++;next} {n=0}' input.txt
  -1.5000    0.004    0.000    0.004
  -1.4953    0.004    0.000    0.004
  -1.4906    0.004    0.000    0.004
  .......    .....    .....    .....
   0.3609    0.562    5.513    0.562
   0.3656    0.381    5.515    0.381
   0.3703    0.149    5.517    0.149


第二个模式可以简化为
awk-v RS='NR==3'文件
将打印第三个以空行分隔的文本块,从而生成所需的输出-有什么原因不能这样做吗?@EdMorton Good one。我把它弄得太复杂了…@EdMorton这很好,但是我要查找的文本块被错误地放在一个巨大的文本文件中,只能通过这一行“Total DOS and NOS and partial(it)DOSUP…”来识别。这一行“E Total 1”不是唯一的,不能使用。@EdMorton。。。唉,你又这么做了。你醒着的时候我为什么还要回答问题-Dsecond模式可以简化为
awk-v RS='NR==3'文件
将打印第三个以空行分隔的文本块,从而生成所需的输出-有什么原因不能这样做吗?@EdMorton很好。我把它弄得太复杂了…@EdMorton这很好,但是我要查找的文本块被错误地放在一个巨大的文本文件中,只能通过这一行“Total DOS and NOS and partial(it)DOSUP…”来识别。这一行“E Total 1”不是唯一的,不能使用。@EdMorton。。。唉,你又这么做了。你醒着的时候我为什么还要回答问题-D@glanz-你能澄清一下吗?对我来说,考虑到你问题中的输入数据,这产生了你在“我想要得到”下面提到的输出。七行,两块三行四列,用点分隔。没有别的了。您的实际数据是否可能在模式后有两个空行,而不是只有一个空行?我认为
{n=0}
块只有在
n
已经
0
时才会被命中,因此您可以将其删除,或者,如果您在打印第一个目标块后试图重置它,请想出一些其他逻辑。@ghoti@Ed-您的答案完全正确,并且解释得很好。我终于明白了为什么我的原始数据不起作用。这个