linux - 从每组连续的匹配行中提取第一行
问题描述
我有一个如下所示的数据文件:
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 脚本呢?有没有一种不太模糊的方法来做到这一点?
解决方案
您可以将 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
参数:
-d ''
: 使用空字符串作为分隔符(= ASCII NUL 字符)。这相当于-d $'\0'
(参见https://unix.stackexchange.com/q/61029/283498)。-r
:不要解释行中的任何反斜杠(参见https://unix.stackexchange.com/q/192786/283498)。match
: 只是我选择的一个变量名,在循环体中使用。
并且在循环体中:head -n1 <<< "$match"
仅打印当前匹配的第一行(head
带有-n 1
打印其输入的第一行的命令)。旁注:<<<
是 bashism ; 该命令相当于echo "$match" | head -n1
.