我需要能够搜索字符串(让我们使用4320101),在字符串上方打印20行并在此之后打印,直到找到字符串
例如:
Random text I do not want or blank line
16 Apr 2013 00:14:15
id="4320101"
</eventUpdate>
Random text I do not want or blank line
我只想将以下结果输出到文件中:
16 Apr 2013 00:14:15
id="4320101"
</eventUpdate>
我想要的文件中有多个这类文本示例。
我尝试使用以下内容:
cat filename | grep "</eventUpdate>" -A 20 4320101 -B 100 > greptest.txt
但它只会在字符串的两侧显示20行。
注意:
- 文本所在的行号是不一致的,所以我不能脱离这些,因此为什么我使用-A 20.
- 理想情况下我宁愿拥有它,所以当它在字符串后搜索时,它会在它停止时找到并继续搜索。
总结:找到4320101,在4320101(或一行空白行)上输出20行,然后输出4320101以下的所有行到
</eventUpdate>
进行研究我不确定如何让awk,nawk或sed对我有利于这样做。
答案 0 :(得分:1)
这是一个丑陋的awk
解决方案:)
awk 'BEGIN{last=1}
{if((length($0)==0) || (Random ~ $0))last=NR}
/4320101/{flag=1;
if((NR-last)>20) last=NR-20;
cmd="sed -n \""last+1","NR-1"p \" input.txt";
system(cmd);
}
flag==1{print}
/eventUpdate/{flag=0}' <filename>
基本上它的作用是跟踪Random
变量中包含last
模式的最后一个空白行或行。现在,如果找到了4320101
,则会从that line -20 or last
打印出较近的system sed
命令。并设置flag
。 flag
会导致打印下一行以上的行,直到找到eventUpdate
为止。虽然没有测试,但应该正常工作
答案 1 :(得分:1)
sed / awk中的后视总是很棘手..这个自包含的awk
脚本基本上保存了最后20行,当它到达4320101
时它会打印这些存储的行,直到找到空白或不需要的线的点,然后停止。此时它会切换到printall
模式并打印所有行,直到遇到eventUpdate
,然后打印并退出。
awk '
function store( line ) {
for( i=0; i <= 20; i++ ) {
last[i-1] = last[i]; i++;
};
last[20]=line;
};
function purge() {
for( i=20; i >= 0; i-- ) {
if( length(last[i])==0 || last[i] ~ "Random" ) {
stop=i;
break
};
};
for( i=(stop+1); i <= 20; i++ ) {
print last[i];
};
};
{
store($0);
if( /4320101/ ) {
purge();
printall=1;
next;
};
if( printall == 1) {
print;
if( /eventUpdate/ ) {
exit 0;
};
};
}' test
答案 2 :(得分:1)
你可以尝试这样的事情 -
awk '{
a[NR] = $0
}
/<\/eventUpdate>/ {
x = NR
}
END {
for (i in a) {
if (a[i]~/4320101/) {
for (j=i-20;j<=x;j++) {
print a[j]
}
}
}
}' file
答案 3 :(得分:1)
让我们看看我是否理解您的要求:
您有两个字符串,我称之为KEY
和LIMIT
。你要打印:
包含KEY
的行前最多20行,但如果有空行则停止。
包含KEY
的行与包含LIMIT
的以下行之间的所有行。 (这忽略了你的要求,即不超过100条这样的线;如果这很重要,那么添加相对简单。)
完成(1)
的最简单方法是保留20行的循环缓冲区,并在点击key
时将其打印出来。 sed或awk中(2)
是微不足道的,因为你可以使用双地址形式来打印范围。
让我们在awk中做到这一点:
#file: extract.awk
# Initialize the circular buffer
BEGIN { count = 0; }
# When we hit an empty line, clear the circular buffer
length() == 0 { count = 0; next; }
# When we hit `key`, print and clear the circular buffer
index($0, KEY) { for (i = count < 20 ? 0 : count - 20; i < count; ++i)
print buf[i % 20];
hi = 0;
}
# While we're between key and limit, print the line
index($0, KEY),index($0, LIMIT)
{ print; next; }
# Otherwise, save the line
{ buf[count++ % 20] = $0; }
为了实现这一点,我们需要设置KEY
和LIMIT
的值。我们可以在命令行上执行此操作:
awk -v "KEY=4320101" -v "LIMIT=</eventUpdate>" -f extract.awk $FILENAME
备注:强>
我使用index($0, foo)
而不是更常见的/foo/
,因为它避免了必须转义正则表达式特殊字符,并且在要求中没有任何地方甚至需要regexen。 index(haystack, needle)
会在needle
中返回haystack
的索引,索引从1
开始,如果找不到0
则返回needle
。用作真/假值,找到needle
即可。
next
导致当前行的处理结束。它可以非常方便,正如这个小程序所示。
答案 4 :(得分:1)
这可能适合你(GNU sed):
sed ':a;s/\n/&/20;tb;$!{N;ba};:b;/4320102/!D;:c;n;/<\/eventUpdate>/!bc' file
编辑:
:a;s/\n/&/20;tb;$!{N;ba};
这会在模式空间(PS)中保留20行的窗口:b;/4320102!D;
这会将上面的窗口移动到文件中,直到找到模式4320102
。:c;n;/<\/eventUpdate>/!bc
打印20行窗口以及任何后续行,直到找到模式<\/eventUpdate>
。答案 5 :(得分:0)
最简单的方法是使用文件的2次传递 - 第一次用于识别找到目标正则表达式的范围内的行号,第二次用于打印所选范围内的行,例如:
awk '
NR==FNR {
if ($0 ~ /\<4320101\>/ {
for (i=NR-20;i<NR;i++)
range[i]
inRange = 1
}
if (inRange) {
range[NR]
}
if ($0 ~ /<\/eventUpdate>/) {
inRange = 0
}
next
}
FNR in range
' file file