有一个大文件,其中包含在文件中定期重复的模式,我想在出现某些值以及下一个值后仅提取特定模式氮行。
这是一个例子,但之前的数字members of the group
实际上并不存在。
输入:
1 members of the group
...
...
2 members of the group
...
...
...
n members of the group
...
...
...
输出:
85 members of the group
...
...
...
...
...
(第 85 场比赛和接下来的 5 行)
答案1
这里有一种方法awk
:
awk -vN=85 -vM=5 'BEGIN{c=0}
/PATTERN/{c++
{if (c==N) {l=NR;last=NR+M}}
}{if (NR<=last && NR>=l) print}' infile
哪儿N
是氮th 行匹配PATTERN
,M
是后面的行数。它设置一个计数器,当氮遇到第 3 行匹配时,它会保存行号。然后它打印从当前NR
到NR
+ 的行中号。
根据记录,这就是使用sed
(gnu sed
语法) 的方法:
sed -nE '/PATTERN/{x;/\n{84}/{x;$!N;$!N;$!N;$!N;$!N;p;q};s/.*/&\n/;x}' infile
这是利用保持空间来计数。
每次遇到匹配的行时PATTERN
,ex
都会更改缓冲区并检查是否有N-1\n
保持缓冲区中出现ewline 字符。如果检查成功,它会x
再次更改,拉入下一个中号使用$!N
命令并p
打印模式空间,然后q
uits。
否则,它只是将另一个\n
ewline char 添加到保留空间,然后 ex
变回来。
该解决方案不太方便,因为它很快就会变得很麻烦中号是一个很大的数字,需要一些printf
-fu 来构建sed
脚本(更不用说模式和使用一些sed
s 来保持空间限制)。
答案2
(exec <file.txt; grep -m 85 'PATTERN' | tail -n 1; head -n 5)
显然,您可以根据需要调整数字。
从man grep
:
-m NUM, --max-count=NUM Stop reading a file after NUM matching lines. If the input is standard input from a regular file, and NUM matching lines are output, grep ensures that the standard input is positioned to just after the last matching line before exiting, regardless of the presence of trailing context lines. This enables a calling process to resume a search.
上面的命令通过使用子 shell 并将 STDIN 设置为您想要的文件来利用此功能grep
,以便此功能可以正常工作。然后,您可以简单地使用 捕获最终(第 85 个)实例tail -n 1
,并通过单独调用 来获取所需的上下文行head
。
如果您使用此命令知道该文件至少有 85 个实例PATTERN
;在这种情况下它将完美地工作。
如果它可能少了,命令就需要一些调整;在当前状态下,如果匹配项少于您所请求的数量,它将简单地打印最终匹配项,而不会出现尾随上下文行。
答案3
主要不了解awk
和使用sed
正则表达式的东西,我会这样做:
- 用于
grep
查找模式,包括行号 (-n
) - 使用
head
和tail
(或sed
)获得第 85 个匹配项(参见这里) - 使用隔离出行号 N
cut
- 再次使用
head
andtail
(或sed
) 获取原始文件的第 N 行和后续 5 行
所有这些都可以合并为一行。很脏,可能很慢,但可以使用最少的工具集工作。
例子
以下内容搜索 rkhunter.log 文件并显示“basename”的第三个匹配项以及后续四行:
/var/log$ tail rkhunter.log -n +$(grep -n 'basename' rkhunter.log|cut -d: -f1|tail -n +3|head -1)| head -5
编辑
刚刚看到@Wildcard的答案,切换-m
确实grep
比我原来的解决方案更容易使用。所以这是另一个使用的答案grep -m
/var/log$ grep -m 3 -A 4 'basename' rkhunter.log | tail -5
答案4
这在我的 bash 中有效:
{ T=85; N=5; c=0; while read line ; do echo "$line" | grep -c "members of the group" > /dev/null && c=$(($c+1)) ; [[ $c -eq $T ]] && { echo "$line"; break ;} ; done ; head -n $N ; } < input_file