awk脚本:删除模式匹配之前和之后的行,直到空白行

时间:2014-08-13 21:49:54

标签: bash awk gawk

我昨天开始学习awk,试图解决这个问题(并学习一门有用的新语言)。起初我尝试使用sed,但很快意识到它不是访问/操作模式匹配之前的行的正确工具。

我需要:

  1. 删除包含" foo"的所有行(在它上面是微不足道的,但不能跟踪前面的行)
  2. 查找包含" bar"
  3. 的行
  4. 删除包含" bar"
  5. 的行之前的行
  6. 删除包含" bar"的行之后的所有行。直到我们达到空白
  7. 示例输入:

    This is foo stuff
    I like food!
    It is tasty!
    
    stuff
    something
    stuff
    stuff
    This is bar
    Hello everybody
    I'm Dr. Nick
    
    things
    things
    things
    

    期望的输出:

    It is tasty!
    
    stuff
    something
    stuff
    
    things
    things
    things
    

    我的尝试:

    {
        valid=1;             #boolean variable to keep track if x is valid and should be printed
        if ($x ~ /foo/){     #x is valid unless it contains foo 
            valid=0;         #invalidate x so that is doesn't get printed at the end
            next;
        }
        if ($0 ~ /bar/){     #if the current line contains bar
            valid = 0;       #x is invalid (don't print the previous line)
            while (NF == 0){ #don't print until we reach an empty line
                next;
            }
        }
        if (valid == 1){     #x was a valid line
            print x;                        
        }
        x=$0;                #x is a reference to the previous line
    }
    

    超级奖励积分(不需要解决我的问题,但我很有兴趣学习如何做到这一点):

    1. 能够在模式匹配之前删除n行
    2. 在输出中包含/排除空白行的选项

4 个答案:

答案 0 :(得分:2)

下面是一个使用模式&的替代awk脚本。用于触发状态更改和管理输出的函数,这会产生相同的结果。

function show_last() {
  if (!skip && !empty) {
    print last
  }
  last = $0
  empty = 0
}
function set_skip_empty(n) {
  skip = n
  last = $0
  empty = NR <= 0
}
BEGIN  { set_skip_empty(0)        }
END    { show_last() ;            }
/foo/  { next;                    }
/bar/  { set_skip_empty(1) ; next }
/^ *$/ { if (skip > 0) { set_skip_empty(0); next } else show_last() }
!/^ *$/{ if (skip > 0) { next }                    else show_last() }

这可以通过在变量last中保留“当前”行来实现 忽略或输出,具体取决于其他事件,例如foobar的出现。

empty变量会跟踪last变量是否确实存在 一个空行,或从开始时的简单空(例如BEGIN)。

要完成“奖励积分”,请将last替换为一系列行,然后根据需要累积N行数。

要排除空行(例如终止bar过滤器的行),请将empty测试替换为last变量长度的测试。在awk中,空行没有长度(但是,带有空格或标签的行 * do * 具有长度)

function show_last() {
  if (!skip && length(last) > 0) {
    print last
  }
  last = $0
}

将导致无空行输出。

答案 1 :(得分:2)

以字符串形式读取每个以空白行分隔的段落,然后执行gsub()删除与您关注的模式匹配RE的字符串:

$ awk -v RS= -v ORS="\n\n" '{ gsub(/[^\n]*foo[^\n]*\n|\n[^\n]*\n[^\n]*bar.*/,"") }1' file
It is tasty!

stuff
something
stuff

things
things
things

要删除N行,请将[^\n]*\n更改为([^\n]*\n){N}

要删除部分RE,请使用GNU awk并使用gensub()代替gsub()

要删除空白行,请更改ORS

的值

玩它......

答案 2 :(得分:1)

一种方式:

awk '
      /foo/ { next }     
 flag && NF { next }     
flag && !NF { flag = 0 }      
      /bar/ { delete line[NR-1]; idx-=1; flag = 1; next } 
            { line[++idx] = $0 }
END {
    for (x=1; x<=idx; x++) print line[x]
}' file
It is tasty!

stuff
something
stuff

things
things
things
  • 如果行包含foo,请跳过它。
  • 如果启用了标记且行不为空,则跳过它。
  • 如果启用了标记且行为空,则禁用标记。
  • 如果行包含bar删除上一行,请重置计数器,启用该标记并跳过它
  • 以递增数字
  • 存储以数组为目标管理的所有行
  • END块中打印线条。

旁注:

  • 要在模式匹配之前删除n行数,可以创建循环。从当前行号开始并使用反向for循环,您可以从临时缓存(数组)中删除行。然后,您可以从自定义的计数器变量中减去n

  • 要包含或排除空行,​​您可以使用NF变量。对于典型行,NF变量设置为基于字段分隔符的字段数。对于空行,此变量为0.例如,如果您在上面的答案中将END块上方的行修改为NF { line[++idx] = $0 },您将看到我们已绕过输出中的所有空白行。

    < / LI>

答案 3 :(得分:1)

这个awk应该可以在不将完整文件存储在内存中的情况下工作:

awk '/bar/{skip=1;next} skip && p~/^$/ {skip=0} NR>1 && !skip && !(p~/foo/){print p} {p=$0} 
    END{if (!skip && !(p~/foo/)) print p}' file

It is tasty!

stuff
something
stuff

things
things
things