我有一个问题,我想解析一个文件的输出,我想在两个模式之间抓取第n个文本,最好使用awk或sed
category
1
s
t
done
category
2
n
d
done
category
3
r
d
done
category
4
t
h
done
我们只想说这个例子我想在类别和完成之间抓取第三次出现的文本,基本上输出将是
category
3
r
d
done
答案 0 :(得分:8)
这可能适合你(GNU sed):
'sed -n '/category/{:a;N;/done/!ba;x;s/^/x/;/^x\{3\}$/{x;p;q};x}' file
使用-n
选项关闭自动打印。收集category
和done
之间的行。将计数器存储在保留空间中,当它达到3时,在模式空间中打印该集合并退出。
或者如果您更喜欢awk:
awk '/^category/,/^done/{if(++m==1)n++;if(n==3)print;if(/^done/)m=0}' file
答案 1 :(得分:5)
尝试这样做:
awk -v n=3 '/^category/{l++} (l==n){print}' file.txt
或者更神秘:
awk -v n=3 '/^category/{l++} l==n' file.txt
如果您的文件很大:
awk -v n=3 '/^category/{l++} l>n{exit} l==n' file.txt
答案 2 :(得分:1)
如果您的文件不包含任何空字符,则此处正在使用GNU sed
。这将找到模式范围的第三次出现。但是,您可以轻松地修改它以获得您想要的任何事件。
sed -n '/^category/ { x; s/^/\x0/; /^\x0\{3\}$/ { x; :a; p; /done/q; n; ba }; x }' file.txt
结果:
category
3
r
d
done
说明:
使用-n
开关关闭默认打印。在一行的开头匹配“类别”一词。使用保留空间交换模式空间,并在模式的开头附加空字符。在该示例中,如果模式包含两个前导空字符,则将模式拉出holdspace。现在创建一个循环并打印模式空间的内容,直到最后一个模式匹配。找到最后一个模式后,sed
将退出。如果未找到,sed
将继续读取下一行输入并继续其循环。
答案 3 :(得分:1)
awk -v tgt=3 '
/^category$/ { fnd=1; rec="" }
fnd {
rec = rec $0 ORS
if (/^done$/) {
if (++cnt == tgt) {
printf "%s",rec
exit
}
fnd = 0
}
}
' file
答案 4 :(得分:0)
使用GNU awk,您可以将记录分隔符设置为正则表达式:
<file awk 'NR==n+1 { print rt, $0 } { rt = RT }' RS='\\<category' ORS='' n=3
输出:
category
3
r
d
done
RT
是匹配的记录分隔符。请注意,相对于n
的记录将会被关闭,因为第一个记录指的是第一个RS
之前的记录。
根据Ed的评论,当记录中包含其他数据时,这将不起作用,例如:
category
1
s
t
done
category
2
n
d
done
foo
category
3
r
d
done
bar
category
4
t
h
done
解决这个问题的一种方法是使用第二个(或第一个)awk来清理输入:
<file awk '/^category$/,/^done$/' |
awk 'NR==n+1 { print rt, $0 } { rt = RT }' RS='\\<category' ORS='' n=3
输出:
category
3
r
d
done
正如 Ed 在评论中指出的那样,上述方法不会搜索结束模式。执行此操作的一种方法是getline
(请注意,有一些caveats使用awk getline):
<file awk '
/^category$/ {
v = $0
while(!/^done$/) {
if(!getline)
exit
v = v ORS $0
}
if(++nr == n)
print v
}' n=3
在一行:
<file awk '/^category$/ { v = $0; while(!/^done$/) { if(!getline) exit; v = v ORS $0 } if(++nr == n) print v }' n=3