【问题标题】:Grep the first line from each contiguous group of matching lines从每组连续的匹配行中提取第一行
【发布时间】:2021-10-09 17:09:07
【问题描述】:

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

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 脚本呢?有没有一种不太模糊的方法来做到这一点?

【问题讨论】:

  • 我不清楚您的问题是关于 bash 还是关于提供给 grep 的(多行)正则表达式?
  • 我只想说这个问题对我来说很清楚,我不会关闭它。
  • 我正在写一个无法再发布的答案:grep -Pzo '([^\n]*interesting line[^\n]*data[^\n]*\n)+' file | while IFS='' read -d '' -r match; do head -n1 <<< "$match"; done。简而言之:使用带有 NUL 分隔匹配项的贪婪正则表达式的 grep,同时读取每个匹配项以仅打印它们的第一行。
  • @ArkadiuszDrabczyk 感谢您的评论,在编辑之前可能不太清楚,但是当投票和关闭过程开始时,很难改变它。
  • 如果问题被重新打开,另一个可能的答案:awk '/^interesting/ {if (NR>ingroup) print; ingroup=NR+1}'

标签: linux command-line


【解决方案1】:

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

read参数:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-30
    • 1970-01-01
    • 2019-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-29
    相关资源
    最近更新 更多