首页 > 解决方案 > 从每组连续的匹配行中提取第一行

问题描述

我有一个如下所示的数据文件:

a separator
interesting line 1
interesting line 2
a comment
interesting line 3
interesting line 4
interesting line 5
a non interesting line
some other data
interesting line 6
.
.
.

并且我想interesting line从每个连续的组中提取第一个,无论组中有多少行或有多少额外的行将这些组分开。

对于输出上方的测试输入将是:

interesting line 1
interesting line 3
interesting line 6

我可以在 python 中轻松地做到这一点,方法是在我匹配一行时触发一个状态变量,并在遇到不匹配的行时重置,但是单行 shell 脚本呢?有没有一种不太模糊的方法来做到这一点?

标签: linuxcommand-line

解决方案


您可以将 grep 与贪婪的正则表达式一起使用,然后使用以下命令打印每个匹配项的第一行:

grep -Pzo '([^\n]*interesting line[^\n](\n|$))+' file |
  while IFS='' read -d '' -r match
  do
    head -n1 <<< "$match"
  done

grep参数:

  • -P: 对正则表达式中的 \n 使用 Perl 兼容正则表达式(而不是默认的基本正则表达式)。
  • -z:将输入视为一组行,每行都以零字节结尾。ASCII NUL 字符将分隔每个匹配项,使我们能够可靠地分隔匹配项。
  • 正则表达式([^\n]*blablabla[^\n]*(\n|$))+将匹配每组包含 blablabla 的连续行。

在 while 条件命令中,IFS 为read. 否则,使用默认的 IFS,每个匹配项的最后一个换行符将被吃掉read(这可能不是问题)。始终在“读取时”清除 IFS 以使变量中的文本与读取时完全相同是一个好习惯(前导空格也很容易被吃掉)。

read参数:

并且在循环体中:head -n1 <<< "$match"仅打印当前匹配的第一行(head带有-n 1打印其输入的第一行的命令)。旁注:<<<是 bashism ; 该命令相当于echo "$match" | head -n1.


推荐阅读