From 1ee53ee9f9ce56f5732be070c1457df4d57847d7 Mon Sep 17 00:00:00 2001 From: Michael Mikonos <127171689+mknos@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:22:01 +0800 Subject: [PATCH] grep: implement -Bn and -Cn * For -B, context lines before a matching line are saved in a buffer and emptied when a match occurs * -C can then be added with no extra logic as a shortcut for -A and -B * The old meaning of -C goes away, but I'm not aware of any other version of grep that had that special count feature * To test, I modified the file "ar" so it has two consecutive lines matching "this" %perl grep -n -C 5 this ar 452-on the command line to ``select'' archive files for an operation, only 453-the first file with a matching name will be selected. 454- 455-The normal use of ar is for the creation and maintenance of libraries 456-suitable for use with the loader (see ld(1)) although it is not 457:this is good 458:restricted to this purpose. 459- 460-=head2 OPTIONS 461- 462-I accepts the following options: 463- -- 571-Extract the specified archive members into the files named by the 572-command line arguments. If no members are specified, all the 573-owner and group will be unchanged. The file access and modifica- 574-tion times are the time of the extraction (but see the -B op- 575-tion). The file permissions will be set to those of the file 576:when it was entered into the archive; this will fail if the user 577-is not the owner of the extracted file or the super-user. 578- 579-=back 580- 581-I exits 0 on success, and >0 if an error occurs. -- 596-=head1 COPYRIGHT and LICENSE 597- 598-This program is copyright by dkulp 1999. 599- 600-This program is free and open software. You may use, copy, modify, distribute 601:and sell this program (and any modified variants) in any way you wish, 602-provided you do not restrict others to do the same. 603- 604-=cut 605- %perl grep -n -C 1 this ar 456-suitable for use with the loader (see ld(1)) although it is not 457:this is good 458:restricted to this purpose. 459- -- 575-tion). The file permissions will be set to those of the file 576:when it was entered into the archive; this will fail if the user 577-is not the owner of the extracted file or the super-user. -- 600-This program is free and open software. You may use, copy, modify, distribute 601:and sell this program (and any modified variants) in any way you wish, 602-provided you do not restrict others to do the same. -- --- bin/grep | 60 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/bin/grep b/bin/grep index 248db571..1a74f385 100755 --- a/bin/grep +++ b/bin/grep @@ -54,7 +54,7 @@ use File::Spec; use File::Temp qw(); use Getopt::Std; -our $VERSION = '1.012'; +our $VERSION = '1.013'; $| = 1; # autoflush output @@ -88,14 +88,13 @@ sub VERSION_MESSAGE { sub usage { die <1 match per line possible -w word boundaries only -q quiet; nothing is written to standard output -x exact matches only @@ -113,6 +112,8 @@ Options: -r recursive on directories or dot if none -p paragraph mode (default: line mode) -P ditto, but specify separator, e.g. -P '%%\\n' + -C show lines of context around each matching line + -B show lines before each matching line -A show lines after each matching line -a treat binary files as plain text files -s suppress errors for failed file and dir opens @@ -147,11 +148,6 @@ sub parse_args { $Matches++ if m/$pattern/; } }; - my $cls_grep_C = sub { - for my $pattern (@patterns) { - $Matches++ while m/$pattern/g; - } - }; my $cls_grep_v = sub { for my $pattern (@patterns) { $Matches += !/$pattern/; @@ -249,13 +245,18 @@ sub parse_args { @ARGV = @tmparg; $opt{'p'} = $opt{'P'} = ''; # argument to print() - getopts('inCcwsxvHhe:f:LlgurpP:aqTFZm:A:', \%opt) or usage(); + getopts('inC:cwsxvHhe:f:LlgurpP:aqTFZm:A:B:', \%opt) or usage(); if (defined $opt{'m'} && $opt{'m'} !~ m/\A[0-9]+\z/) { die "$Me: invalid max count\n"; } - if (defined $opt{'A'} && $opt{'A'} !~ m/\A[0-9]+\z/) { - die "$Me: bad line count for -A\n"; + foreach my $o (qw(A B C)) { + if (defined $opt{$o} && $opt{$o} !~ m/\A[0-9]+\z/) { + die "$Me: bad context count for -$o\n"; + } + } + if (defined $opt{'C'}) { + $opt{'A'} = $opt{'B'} = $opt{'C'}; } $opt{'l'} = 0 if $opt{'L'}; my $no_re = $opt{F} || ( $Me =~ /\bfgrep\b/ ); @@ -355,13 +356,11 @@ sub parse_args { $opt{w} && ( @patterns = map { '(?:\b|(?!\w))' . $_ . '(?:\b|(?) { + my $ctxb_dump = 0; if (defined $opt->{'m'}) { # maximum may be zero last LINE if $total >= $opt->{'m'} && $ctx_a == 0; } @@ -470,12 +471,26 @@ FILE: while ( defined( $file = shift(@_) ) ) { &{$matcher}(); # do it! (check for matches) ############## + if (!$Matches && defined($opt->{'B'})) { + push @ctx_b, $_; + shift @ctx_b if scalar(@ctx_b) > $opt->{'B'}; + } if ($ctx_a > 0) { $ctx_a--; # show context line } elsif (!$Matches) { next LINE; } + if ($Matches && @ctx_b) { + $ctxb_dump = 1; + my $n = $. - $opt->{'B'}; + foreach my $bline (@ctx_b) { + print($n++, '-') if $opt->{'n'}; + print $bline; + } + @ctx_b = (); + } + $total += $Matches; last FILE if $opt->{'q'}; # single match for all files last LINE if $opt->{'L'}; # one match is enough @@ -515,6 +530,9 @@ FILE: while ( defined( $file = shift(@_) ) ) { if ($ctx_a == 0 && !$Matches) { print "--\n"; } + if ($ctxb_dump && $ctx_a == 0) { + print "--\n"; + } } close $fh; } @@ -540,8 +558,8 @@ grep - search for regular expressions and print =head1 SYNOPSIS - grep [-incCwsxvhHlLFigurpaqT] [-e pattern] [-A NUM] [-m NUM] - [-f pattern-file] [-P sep] [pattern] [file ...] + grep [-incwsxvhHlLFigurpaqT] [-e pattern] [-A NUM] [-B NUM] [-C NUM] + [-m NUM] [-f pattern-file] [-P sep] [pattern] [file ...] =head1 DESCRIPTION @@ -575,11 +593,13 @@ Display NUM lines of context after each matching line. List matching lines from binary files as if they were plain text files. -=item B<-C> +=item B<-B> I + +Display NUM lines of context before each matching line. + +=item B<-C> I -Output the count of the matching lines or paragraphs. This is similar -to the B<-c> option (in fact, it implies the B<-c> option), except more -than one match is possible in each line or paragraph. +Display NUM lines of context surrounding each matching line. =item B<-c>