如何查找或搜索单词 PATTERN1 和 PATTERN2 并稍后打印它们 我想搜索 PATTERN3 并打印搜索 PATTERN2/PATTERN3 之间的整个内容或部分(注意 Pattern1/2/3 在文件中多次出现)
输入文件
other lines
...
####Pattern 1####
...
other lines
...
####Pattern 2####
line 1
line 2
line 3
line 4
line 5
line ...
####Pattern 3####
...
other lines
####Pattern 1####
...
other lines
####Pattern 2####
line 1
line 2
line 3
line 4
####Pattern 3####
...
other lines
输出 l 需要的是
####Pattern 1####
####Pattern 2####
line 1
line 2
line 3
line 4...etc
####Pattern 3####
答案1
现在您已经指定我们需要跳过所有可能出现的不符合预期顺序的虚假模式,我们需要一些更复杂的东西,例如有限状态机。
因此,这是一个更复杂的示例输入文件,其中包括所有奇怪的边缘情况和注释:
a
#### Pattern 2 #### Ignore because we have not yet seen Pattern 1
z
#### Pattern 3 #### Ignore because we have not yet seen Pattern 1, 2
b
#### Pattern 1 #### (!!)
#### Pattern 1 #### Don't print 1 in between 1-2
c
d
#### Pattern 2 #### (!!)
e
#### Pattern 1 #### Don't print 1 in between 2-3
#### Pattern 2 #### ?? Don't print 2 in between 2-3 ??
f
#### Pattern 3 #### (!!)
?? Now reset and accept look for the start of a NEW 1,2,3 cycle. Right ??
g
#### Pattern 3 #### Ignore
#### Pattern 2 #### Ignore
#### Pattern 3 #### Ignore
h
#### Pattern 1 #### (!!)
i
#### Pattern 3 #### Don't print 3 in between 1-2
j
#### Pattern 2 #### (!!)
k
l
#### Pattern 3 #### (!!)
m
n
预期输出:
#### Pattern 1 #### (!!)
#### Pattern 2 #### (!!)
e
f
#### Pattern 3 #### (!!)
#### Pattern 1 #### (!!)
#### Pattern 2 #### (!!)
k
l
#### Pattern 3 #### (!!)
所以看起来我们需要构建一个具有 3 个状态的有限状态机。所以现在我们正在编写一个完整的脚本......
#!/usr/bin/perl
use warnings;
use strict;
## Linear state machine 0 --> 1 --> 2 --> 0
my @patterns = (
qr(Pattern 1), # state 0: noprint; match this: noprint && state = 1
qr(Pattern 2), # state 1: noprint; match this: print && state = 2
qr(Pattern 3) # state 2: print (NOT patterns); match this: print && stage = 0
);
my $state = 0;
while (<>) {
if (0 == $state) {
if (m/$patterns[0]/) {
++$state;
}
} elsif (1 == $state) {
if (m/$patterns[1]/) {
print;
++$state;
}
} elsif (2 == $state) {
if (m/$patterns[0]/ || m/$patterns[1]/) {
# Ignore
} elsif (m/$patterns[2]/) {
print;
$state = 0;
} else {
print;
}
} else {
die "Bad programmer! ($state)";
}
}
有点丑。可以使用哈希来实现更灵活的状态机,
[$state_num, $pattern_num] => sub { ...action... }
其中跳过/忽略是未出现在哈希中的任何[状态,模式]组合的默认操作。但这留给热心读者作为练习;-)
答案2
使用 sed 非常简单:
sed -n '/Pattern 1/p; /Pattern 2/,/Pattern 3/p' file
####Pattern 1####
####Pattern 2####
line 1
line 2
line 3
line 4
line 5
line ...
####Pattern 3####
####Pattern 1####
####Pattern 2####
line 1
line 2
line 3
line 4
####Pattern 3####
给定 Douglas 的输入文件,我们可以让 awk 产生他的预期输出。就像他的答案一样,这是一个状态机,它使用几个逻辑变量来确定状态:
awk -v p1="#### Pattern 1 ####" \
-v p2="#### Pattern 2 ####" \
-v p3="#### Pattern 3 ####" '
$0 ~ p1 && !have_p1 && !in_p2p3 {have_p1 = $0}
$0 ~ p2 && have_p1 && !in_p2p3 {in_p2p3 = 1; print have_p1; print}
have_p1 && in_p2p3 && $0 !~ p1 && $0 !~ p2 {print}
$0 ~ p3 && in_p2p3 {in_p2p3 = 0; have_p1 = ""}
' file
#### Pattern 1 #### (!!)
#### Pattern 2 #### (!!)
e
f
#### Pattern 3 #### (!!)
#### Pattern 1 #### (!!)
#### Pattern 2 #### (!!)
k
l
#### Pattern 3 #### (!!)
答案3
这是执行此操作的一种方法,它会遍历输入文件两次。第一次它仅查看模式线,并从中确定状态转换并记下所需的:1->2->3。
code=$(< INPUT_FILE \
sed -e '/Pattern [1-3]/!d;=' |\
sed -e 'N;s/\n/:/' |\
sed -e '
/Pattern 1/!d;$d;N
/\n.*Pattern 1/D
/Pattern 2/!d;$d;N
/Pattern 3/!d
' |\
sed -e '
s/:.*//;N;s///;N;s///
s/\n.*\n/,/;s/$/p/
'
)
# and having computed the right ranges to print, we now enter the 2nd pass
sed -ne "$code" inp |\
sed -e '/Pattern 1/,/Pattern 2/!b' -e '//!d'
如果您只想调用一次sed
,也可以这样做,如下所示:
sed -e '
/Pattern 1/,/Pattern 2/!d ;# reject non-interesting range
/Pattern 1/h ;# store in hold beginning of range
/Pattern [23]/H ;# store pattern 2 and 3 lines in hold too
/Pattern 2/!d ;# not at end of range ... delete
g;/Pattern 3/d ;# range seen completed, now check whether pattern 3 came
;# during the 1->2 search, and delete everything & restart
;# afresh if it did. otherwise, empty the pattern space in
;# preparation for reading the 2->3
s/.*//
:loop ;# setup a while(1) loop to read 2->3 range
$d;N ;# read the next line into the pattern space provided
;# it isnt the last
/Pattern [12]/d ;# if we encounter pattern 1/2 then drop everything & start afresh
/Pattern 3/{ ;# we checked 1/2 didnot come and 3 came
s/^\n//;H;g;b
}
bloop
' input-file.txt
这是基于 FSM 的方法,用于hashes
编码当前状态、下一状态关系(Mealy 机器公式):
perl -lne '
BEGIN {
sub getLinetype {
local $_ = @_ ? shift : $_;
return
/Pattern 1/ ? "PATT_1" :
/Pattern 2/ ? "PATT_2" :
/Pattern 3/ ? "PATT_3" :
"NON_PATT_123";
}
# ---------------------------------------------------------------------
# PS line_type NS action
# ---------------------------------------------------------------------
$h{ RESET }{ PATT_1 } = [ "STATE1", sub { @A = ( $_ ) } ];
$h{ RESET }{ PATT_2 } = [ "RESET", sub { @A = () } ];
$h{ RESET }{ PATT_3 } = [ "RESET", sub { @A = () } ];
$h{ RESET }{ NON_PATT_123 } = [ "RESET", sub { @A = () } ];
# ---------------------------------------------------------------------
$h{ STATE1 }{ PATT_1 } = [ "STATE1", sub { @A = ( $_ ) } ];
$h{ STATE1 }{ PATT_2 } = [ "STATE2", sub { push @A, $_ } ];
$h{ STATE1 }{ PATT_3 } = [ "RESET", sub { @A = () } ];
$h{ STATE1 }{ NON_PATT_123 } = [ "STATE1", sub { ; } ];
# ---------------------------------------------------------------------
$h{ STATE2 }{ PATT_1 } = [ "STATE1", sub { @A = ( $_ ) } ];
$h{ STATE2 }{ PATT_2 } = [ "RESET", sub { @A = () } ];
$h{ STATE2 }{ PATT_3 } = [ "STATE3", sub { print for splice(@A), $_ } ];
$h{ STATE2 }{ NON_PATT_123 } = [ "STATE2", sub { push @A, $_ } ];
# ---------------------------------------------------------------------
$h{ STATE3 }{ PATT_1 } = [ "STATE1", sub { @A = ( $_ ) } ];
$h{ STATE3 }{ PATT_2 } = [ "RESET", sub { @A = () } ];
$h{ STATE3 }{ PATT_3 } = [ "RESET", sub { @A = () } ];
$h{ STATE3 }{ NON_PATT_123 } = [ "RESET", sub { @A = () } ];
# ---------------------------------------------------------------------
$present_state = "RESET";
}
my $line_type = getLinetype();
my $next_state = $h{$present_state}{$line_type}->[0];
my $action_ref = $h{$present_state}{$line_type}->[1];
$action_ref->();
$present_state = $next_state;
' input-file.txt