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
hashes
이는 현재 상태와 다음 상태 관계를 인코딩 하는 데 사용되는 FSM 기반 방법입니다 (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