Skip to content

Commit b24bf24

Browse files
authored
grep: implement -Bn and -Cn (#811)
* 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<ar> 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<o> 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<ar> 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. --
1 parent 529d42b commit b24bf24

File tree

1 file changed

+40
-20
lines changed

1 file changed

+40
-20
lines changed

bin/grep

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ use File::Spec;
5454
use File::Temp qw();
5555
use Getopt::Std;
5656

57-
our $VERSION = '1.012';
57+
our $VERSION = '1.013';
5858

5959
$| = 1; # autoflush output
6060

@@ -88,14 +88,13 @@ sub VERSION_MESSAGE {
8888

8989
sub usage {
9090
die <<EOF;
91-
usage: $Me [-inCcwsxvHhLlFgurpaqT] [-e pattern] [-A NUM] [-m NUM]
92-
[-f pattern-file] [-P sep] [pattern] [file...]
91+
usage: $Me [-incwsxvHhLlFgurpaqT] [-e pattern] [-A NUM] [-B NUM] [-C NUM]
92+
[-m NUM] [-f pattern-file] [-P sep] [pattern] [file...]
9393
9494
Options:
9595
-i case insensitive
9696
-n number lines
9797
-c give count of lines matching
98-
-C ditto, but >1 match per line possible
9998
-w word boundaries only
10099
-q quiet; nothing is written to standard output
101100
-x exact matches only
@@ -113,6 +112,8 @@ Options:
113112
-r recursive on directories or dot if none
114113
-p paragraph mode (default: line mode)
115114
-P ditto, but specify separator, e.g. -P '%%\\n'
115+
-C show lines of context around each matching line
116+
-B show lines before each matching line
116117
-A show lines after each matching line
117118
-a treat binary files as plain text files
118119
-s suppress errors for failed file and dir opens
@@ -147,11 +148,6 @@ sub parse_args {
147148
$Matches++ if m/$pattern/;
148149
}
149150
};
150-
my $cls_grep_C = sub {
151-
for my $pattern (@patterns) {
152-
$Matches++ while m/$pattern/g;
153-
}
154-
};
155151
my $cls_grep_v = sub {
156152
for my $pattern (@patterns) {
157153
$Matches += !/$pattern/;
@@ -249,13 +245,18 @@ sub parse_args {
249245
@ARGV = @tmparg;
250246

251247
$opt{'p'} = $opt{'P'} = ''; # argument to print()
252-
getopts('inCcwsxvHhe:f:LlgurpP:aqTFZm:A:', \%opt) or usage();
248+
getopts('inC:cwsxvHhe:f:LlgurpP:aqTFZm:A:B:', \%opt) or usage();
253249

254250
if (defined $opt{'m'} && $opt{'m'} !~ m/\A[0-9]+\z/) {
255251
die "$Me: invalid max count\n";
256252
}
257-
if (defined $opt{'A'} && $opt{'A'} !~ m/\A[0-9]+\z/) {
258-
die "$Me: bad line count for -A\n";
253+
foreach my $o (qw(A B C)) {
254+
if (defined $opt{$o} && $opt{$o} !~ m/\A[0-9]+\z/) {
255+
die "$Me: bad context count for -$o\n";
256+
}
257+
}
258+
if (defined $opt{'C'}) {
259+
$opt{'A'} = $opt{'B'} = $opt{'C'};
259260
}
260261
$opt{'l'} = 0 if $opt{'L'};
261262
my $no_re = $opt{F} || ( $Me =~ /\bfgrep\b/ );
@@ -355,13 +356,11 @@ sub parse_args {
355356
$opt{w} && ( @patterns = map { '(?:\b|(?!\w))' . $_ . '(?:\b|(?<!\w))' } @patterns );
356357
$opt{'x'} && ( @patterns = map {"^$_\$"} @patterns );
357358
$opt{'g'} ||= $opt{'u'};
358-
$opt{'c'} ||= $opt{'C'};
359359

360360
foreach (@patterns) {s(/)(\\/)g}
361361

362362
if ($opt{'g'}) { $matcher = $cls_grep_g; }
363363
elsif ($opt{'v'}) { $matcher = $cls_grep_v; }
364-
elsif ($opt{'C'}) { $matcher = $cls_grep_C; }
365364
else { $matcher = $cls_grep; }
366365

367366
return ( \%opt, $matcher );
@@ -459,8 +458,10 @@ FILE: while ( defined( $file = shift(@_) ) ) {
459458

460459
$total = $Matches = 0;
461460
my $ctx_a = 0;
461+
my @ctx_b;
462462

463463
LINE: while (<$fh>) {
464+
my $ctxb_dump = 0;
464465
if (defined $opt->{'m'}) { # maximum may be zero
465466
last LINE if $total >= $opt->{'m'} && $ctx_a == 0;
466467
}
@@ -470,12 +471,26 @@ FILE: while ( defined( $file = shift(@_) ) ) {
470471
&{$matcher}(); # do it! (check for matches)
471472
##############
472473

474+
if (!$Matches && defined($opt->{'B'})) {
475+
push @ctx_b, $_;
476+
shift @ctx_b if scalar(@ctx_b) > $opt->{'B'};
477+
}
473478
if ($ctx_a > 0) {
474479
$ctx_a--; # show context line
475480
} elsif (!$Matches) {
476481
next LINE;
477482
}
478483

484+
if ($Matches && @ctx_b) {
485+
$ctxb_dump = 1;
486+
my $n = $. - $opt->{'B'};
487+
foreach my $bline (@ctx_b) {
488+
print($n++, '-') if $opt->{'n'};
489+
print $bline;
490+
}
491+
@ctx_b = ();
492+
}
493+
479494
$total += $Matches;
480495
last FILE if $opt->{'q'}; # single match for all files
481496
last LINE if $opt->{'L'}; # one match is enough
@@ -515,6 +530,9 @@ FILE: while ( defined( $file = shift(@_) ) ) {
515530
if ($ctx_a == 0 && !$Matches) {
516531
print "--\n";
517532
}
533+
if ($ctxb_dump && $ctx_a == 0) {
534+
print "--\n";
535+
}
518536
}
519537
close $fh;
520538
}
@@ -540,8 +558,8 @@ grep - search for regular expressions and print
540558
541559
=head1 SYNOPSIS
542560
543-
grep [-incCwsxvhHlLFigurpaqT] [-e pattern] [-A NUM] [-m NUM]
544-
[-f pattern-file] [-P sep] [pattern] [file ...]
561+
grep [-incwsxvhHlLFigurpaqT] [-e pattern] [-A NUM] [-B NUM] [-C NUM]
562+
[-m NUM] [-f pattern-file] [-P sep] [pattern] [file ...]
545563
546564
=head1 DESCRIPTION
547565
@@ -575,11 +593,13 @@ Display NUM lines of context after each matching line.
575593
576594
List matching lines from binary files as if they were plain text files.
577595
578-
=item B<-C>
596+
=item B<-B> I<NUM>
597+
598+
Display NUM lines of context before each matching line.
599+
600+
=item B<-C> I<NUM>
579601
580-
Output the count of the matching lines or paragraphs. This is similar
581-
to the B<-c> option (in fact, it implies the B<-c> option), except more
582-
than one match is possible in each line or paragraph.
602+
Display NUM lines of context surrounding each matching line.
583603
584604
=item B<-c>
585605

0 commit comments

Comments
 (0)