打印文件直到第N个匹配项

4

我想要将一个文件分割成几个部分。sed可以用来实现这个功能,例如:

sed -e '0,/expr/d' filename

如果我想在“expr”后分割文件的底半部分,可以使用该命令。但是如果有多个出现次数,并且我想在第n次出现后分割怎么办?我已经找到了解决方案,例如如果我想在第二次出现后进行分割,则:

sed -e '0,/expr/! {/expr/,$d}' filename

将文件中从开头到第二个"expr"出现的位置上半部分提取出来。感叹号(!)告诉它忽略第一个范围,只将大括号中的命令应用于文件的其他部分。

但是对于更一般的情况呢?例如,从倒数第二次出现位置开始。

我一直在使用sed,但我认为awk也会有优雅的解决方案。

3个回答

2

简单的awk解决方案:

  1. 匹配到第$n个并包括它本身的正则表达式:

    awk -vn=$n '{print}/regex/&&!--n{exit}'

  2. 匹配到第$n个但不包括它本身的正则表达式:

    awk -vn=$n '/regex/&&!--n{exit}{print}'

    在上述两个程序中,将n设置为0将打印整个文件。此外,{print}的两种用法都可以改为1;,因为默认操作是{print}。(或者在第二个程序中只使用1。)

    为了完整性:

  3. 匹配到第$n个之后的所有内容:

    awk -vn=$n 'n<=0;/regex/{--n}'

注意:如@mklement0在评论中指出的那样,版本为BSD Awk(也称为“one-true-awk”,由Brian Kernighan编写和维护的版本,据我所知仍在维护中)的命令行选项解析存在错误,直到2010年5月23日之前;这显然包括与Mac OS X一起分发的版本(截至v10.9)。因此,如果您使用这些awk版本之一,则需要编写-v n=$n而不是-vn=$n


很好;如果你将“-vn=$n”重新格式化为“-v n=$n”,它也可以在OS X上工作。 - mklement0
@mklement0:您在哪个版本的OS X上使用哪个版本的awk?-vn=$n语法适用于我尝试过的所有awk版本,包括我认为OS X使用的BSD awk。 - rici
这是awk version 20070501(OS X 10.9)-很奇怪,但这就是awk --version报告的。 - mklement0
@mklement0:对于一个闪亮新操作系统来说,这是一个令人惊讶的旧版本。解析-v选项中的错误显然已在2010年5月23日的版本中修复;请参见此处的更改列表:http://web.mit.edu/freebsd/head/contrib/one-true-awk/FIXES - rici
是的,不幸的是,许多随 OS X 一起提供的 OSS 组件都相当老旧(另一个例子:bash 版本是 bash 3.2.51)- 至少部分原因我认为是政治问题(许可问题)。感谢更新。 - mklement0

2

除了@rici的解决方案外,还有一些更多的awk变体

  1. 匹配到第 $n个(含):

    awk -v n=$n 'p<n; /regex/{p++}' file

  2. 匹配到第 $n个(不含):

    awk -v n=$n '/regex/{p++} p<n' file

  3. 从第 $n个开始(含)

    awk -v n=$n '/regex/{p++} p>=n' file

  4. 从第 $n个开始(不含)

    awk -v n=$n 'p>=n; /regex/{p++}' file


但是对于更一般的情况呢?例如,从倒数第二个匹配项开始。

在这种情况下,简单的方法是使用tac将文件反向读取,执行上述选项,然后再次反向打印。

  1. 从第 $n 个匹配项(含)开始

    tac file | awk -v n=$n 'p<n; /regex/{p++}' | tac

  2. 从第 $n 个匹配项(不含)开始

    tac file | awk -v n=$n '/regex/{p++} p<n' | tac

  3. 一直到第 $n 个匹配项(含)

    tac file | awk -v n=$n '/regex/{p++} p>=n' | tac

  4. 一直到第 $n 个匹配项(不含)

    tac file | awk -v n=$n 'p>=n; /regex/{p++}' | tac


对于OS X用户的注意事项,如@mklement0在评论中指出

  • 可怜的[原装]OS X用户(截至OS X 10.9)没有tac

  • 在OS X上,您可以使用tail -r(请注意,Linux上的tail似乎不支持-r)。


可怜的(OS X 10.9及以上版本的)用户们运气不太好:那里没有 tac 命令。 - mklement0
@mklement0 我不熟悉OS X,只使用过Linux或Windows。那么你如何反转文件内容? - jkshah
感谢你激励我去查找:在OS X上,你可以使用tail -r(请注意,Linux上的tail似乎不支持-r)。 - mklement0
@mklement0感谢您的评论。我已将它们添加到ans中以供任何OS X用户使用。 - jkshah

0
这可能适用于您(GNU sed):
sed -nr 'x;/^X{2}/{x;p;b};x;/REGEXP/{x;s/^/X/;x}' file

这将打印出第二个REGEXP匹配后的任何内容。

注意:REGEXP可能在每行中出现一次或多次,但只会计算一次。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接