ファイル内の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回渡すことです。最初は、モードラインだけが報告状態遷移を決定し、必要な項目(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 Machine Official)。

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

関連情報