我昨天开始学习awk,试图解决这个问题(并学习一门有用的新语言)。起初我尝试使用sed,但很快意识到它不是访问/操作模式匹配之前的行的正确工具。
我需要:
示例输入:
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
}
超级奖励积分(不需要解决我的问题,但我很有兴趣学习如何做到这一点):
答案 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
中保留“当前”行来实现
忽略或输出,具体取决于其他事件,例如foo
和bar
的出现。
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 }
,您将看到我们已绕过输出中的所有空白行。
答案 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