파일에서 3단어 패턴을 grep 또는 검색하여 2번째와 3번째 단어 패턴 사이의 내용과 함께 인쇄하는 방법

파일에서 3단어 패턴을 grep 또는 검색하여 2번째와 3번째 단어 패턴 사이의 내용과 함께 인쇄하는 방법

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

관련 정보