我目前遇到了一个需要帮助解决的问题。即使指向正确的方向也会有所帮助。
我有一个包含很多行的文件,并且只想提取特定的“组”行(如果它们遵循某种模式)。 (行必须以 开头A
,下一行必须以 开头B
,下一行必须以 开头C
)
例如: 模式:按照A
, B
,C
的顺序开始。
输入:
A1
B1
C1
D1
A2
B2
D2
A3
D3
A4
B4
C4
A5
B5
D5
输出:
A1
B1
C1
A4
B4
C4
答案1
另一个解决方案awk
:
awk 'p2~/^A/ && p1~/^B/ && /^C/{print p2 RS p1 RS $0} {p2=p1; p1=$0}'
将perl
整个输入作为单个字符串进行读取:
perl -0777 -ne 'print /^A.*\nB.*\nC.*\n/mg'
和ripgrep它支持方便的多行匹配选项-U
rg -oUN '^A.*\nB.*\nC.*'
在这里,-o
选项仅获取匹配部分,-N
选项是防止输出中的行号
答案2
如果您还想要 Sed 解决方案,这很丑陋,但应该可行。
sed -n '
/^A/{
N
/\nB/!D
N
/\nC/!{
s/\n//
D
}
p
}
' file
-n
告诉 sed 不要打印任何内容,除非它到达p
命令。
如果你理解了第一块,你就可以理解其余的:
/^A/
如果模式空间以 A 开头,N
将下一行追加到模式空间。/\nB/!D
如果模式空间没有后跟 B 的换行符,则删除第一个换行符之前的所有内容,并使用生成的模式空间重新开始,而不读取任何输入。
一班轮:sed -n '/^A/{N;/\nB/!D;N;/\nC/!{s/\n//;D};p}' file
答案3
以下awk
程序应该有效:
awk 's==2{if (/^C/) {s=0; p=p ORS $0; print p} else {s=0}}\
s==1{if (/^B/) {s=2; p=p ORS $0} else {s=0}}\
s==0{if (/^A/) {s=1; p=$0}}' input.txt
这将保留一个内部“状态”标志,s
以查看我们处于序列中的哪个点(0:未找到开始,1:A
找到,2:A
按B
顺序找到),并将文本累积到缓冲区中p
。
- 如果找到
A
和B
,并且当前行以 开头C
,我们将当前行添加到缓冲区并打印它。状态重置为0
- 如果
A
找到,并且当前行以 开头B
,我们将当前行添加到缓冲区并将状态设置为2
(=A
并B
找到) - 如果尚未找到开头,并且当前行以 开头
A
,我们将其添加到缓冲区并将状态设置为1
。
答案4
您可以在 sed 中设置一个状态机,仅当前一个状态引导您 A->B->C 时才前进到下一个状态
否则,砍头并重新开始。
$ sed -e '$d;/\n/d
/^A/N;/\nB/!D
$!N;/\nC/!D
' file
A1
B1
C1
A4
B4
C4
如果你有使用PCRE选项编译的gnu grep,那么下面的grep代码是另一种方式。
$ grep -zoP '(?m)^A.*\nB.*\nC.*\n' file | tr -d '\0'