Puedes hacer algo como esto:
echo date | script -q bigram-trace.txt
Resultado:
$ cat bigram-trace.txt
date
sh-4.4$ date
Sun, Feb 18, 2018 6:22:13 PM
sh-4.4$ exit
O sin tubo:
script -q bigram-trace.txt <<'eof'
date
eof
Теперь, когда вы указали, что нам нужно пропустить все ложные шаблоны, которые могут появиться вне ожидаемого порядка, нам нужно что-то более сложное, например, конечный автомат.
Вот пример гораздо более сложного исходного файла, который включает в себя все странные пограничные случаи и комментарии:
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... }
где пропустить/игнорировать — это действие по умолчанию для любой комбинации [состояние, шаблон], которая не появляется в хэше. Но это остается в качестве упражнения для восторженного читателя;-)
Довольно просто с 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####
Имея входной файл Дугласа, мы можем получить этот 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 #### (!!)
Вот один из способов сделать это, дважды просматривая входной файл. В первый раз он проходит и смотрит только на линии шаблона, и по ним мы определяем переходы состояний и отмечаем требуемое :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
для кодирования текущего -состояния, следующего -отношения состояния (формулировки машины Мили -):
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