SUMMARY: Windowing grep output

From: Spalding, Steve <SSPALDIN_at_mem-ins.com>
Date: Mon, 16 Mar 1998 11:19 -0600

The response to this one was overwhelming. The first point that I should
make is that there is no built in functionality in the grep command to
handle windowing the output (aside from grep -p which shows a whole
paragraph instead of just a line or two before and after). To do this you
have to come up with your own search and window script, and I received a
couple of ready made perl scripts from Tim Janes and Andrew Wagliardo
which I will attach at the end of this message. The DBA who I work next
to was also kind enough to show me the script that we already have on our
system which will handle a request such as this (I'll also attach it to
this message). I should have asked before I emailed.

Dr. Tom Blinn also had a few suggestions:

"You could use grep with the -n option, then use the line numbers in the
set
of lines output to use something like head and tail (or even awk) to
output
the lines in the window you're interested in.

You could use awk (you'd need a simple awk program) to scan through the
file
in question and look for the pattern, keeping a previous line or two in
some
internal awk variables, then if it hit a match, have it output the lines
it
was buffering and the match line as well as the next few lines.

You could use sed to make a copy of the file with the lines you're
interested in modified, then use diff -c (context diff) to display the
delta
in the files, then use awk or the like to strip out the parts that you
made
be different and only display the parts that show the context of the
lines
you're really interested in.

Or you could write your own version of "search", I suppose. There are
(or
at least were) a set of OpenVMS like utilities distributed with an
earlier
release of DIGITAL UNIX as "freeware" for evaluation. I don't remember
how
long ago that was. There may have been a "search" utility as part of
that
package. "


Thanks to all who replied:
Matt Moore
Andy Wagliardo
Dr. Thomas P. Blinn
Ray Stell
Joanna Gaski
Richard A Bemrose
Tim Janes
Matthias E. Johnson
Berry Kercheval
Matt Groener

Here are the scripts:



(From Tim Janes)

#!/usr/local/bin/perl

# Usage: cgrep [-lines] pattern [files]

$context = 3;

if ($ARGV[0] =~ /^-(\d+)$/) {
  $context = $1;
  shift;
}

$pat = shift;
$pat =~ s#/#\\/#g;

$_ = <>;
push(_at_ary,$_);

for ( 1 .. $context) {
  unshift(_at_ary,'');
  $_ = <>;
  push(_at_ary,$_) if $_;
}

eval <<LOOP_END;
  while (\$ary[$context]) {
    if (\$ary[$context] =~ /$pat/) {
      print "-------\n" if \$seq++;
      print \_at_ary,"\n";
    }
    \$_ = <> if \$_;
    shift(\_at_ary);
    push(\_at_ary,\$_);
  }
LOOP_END





(From Andrew Wagliardo)

#!/usr/local/bin/perl
#
# Syntax is : wgrep <window> <pattern> <file> where:
#
# <window> = # of lines either side of pattern line to display.
# <pattern> = pattern to search for (this can be a perl regular
expression,
# just without the /'s)
# <file> = the name of the file you want to grep.
#
# Note : It will only do one file (i.e. no *.c, etc. but with bit of
# hacking you should be able to get this to work also)
#
$window = shift;
$pattern = shift;
$file = shift;

open(FILE,"<$file");

_at_lines = <FILE>;

for ($count=0;$count<=$#lines;$count++)
{
    if ($lines[$count] =~ /$pattern/)
    {
        print
"<*--------------------------------------------------------*>\n";
        for ($i = $window; $i > 0; $i--)
        {
            if ($count-$i>=0)
            {
                print $count-$i+1," : $lines[$count-$i]";
            }
        }
        print "--> : $lines[$count]";
        for ($i = 1; $i < $window+1; $i++)
        {
            if ($count+$i<=$#lines)
            {
                print $count+$i+1," : $lines[$count+$i]";
            }
        }
        print
"<*--------------------------------------------------------*>\n";
    }
}




(The script kgrep which we have on our system)

#!/usr/bin/perl
$before = 3;
$after = 3;
$show_start = 0;
$show_nums = 0;
$sep = "******\n";
$show_fname = 1;
$show_sep = 1;

while (_at_ARGV[0] =~ /^-(\w)(.*)/) {
        $arg = $1;
        if ( $arg eq "s" ) { $show_stars = 1; }
         elsif ( $arg eq "n" ) { $show_nums = 1; }
         elsif ( $arg eq "m" ) { $show_fname = 0; }
         elsif ( $arg eq "d" ) { $show_sep = 1; }
         elsif ( $arg eq "w" ) {
               split(/:/,$2);
               $before = _at__[0] if _at__[0] ne '' ;
               $after = _at__[1] if _at__[1] ne '' ;
               }
         elsif ( $arg eq "p" ) { $before = 0 ; $after = 0 ; $show_sep =
0; }
         elsif ( $arg eq "W" ) { $before = 0 ; $after = 0; }
         elsif ( $arg eq "h" ) { &usage(""); }
        else { &usage("kgrep: invalid option: $ARGV[0]" ); }
        shift
}

&usage("missing regular expression\n") if ! _at_ARGV[0];
$regexp = _at_ARGV[0];
shift;
$regexp =~ s,/,\\/,g ;
if (! _at_ARGV[0]) { _at_ARGV[0] = "STDIN"; }

LOOP:
foreach $file (_at_ARGV) {
        if ($file ne "STDIN" && ! open(NEWFILE,$file)) {
               print STDERR "Can't open $file; skipping it.\n";
               next LOOP;
        }
        $fhandle = $file eq "STDIN" ? STDIN : NEWFILE;
        $lnum = "000000";
        $nbef = 0; $naft = 0;
        $matched = 0; $matched2 = 0;
        &clear_buf(0) if $before > 0 ;
        while (<$fhandle>) {
               ++$lnum;
               if ($matched) {
                if ($_ =~ /$regexp/) {
                        $naft = 0;
                        &print_info(1);
                        print $_;
                }
                else {
                        if ($after > 0 && ++$naft <= $after ) {
                                &print_info(0);
                                print $_;
                        }
                        else {
                                $matched = 0; $naft = 0;
                                push(_at_line_buf, $_); $nbef++;
                        }
               }
        }
        else {
               if ($_ =~ /$regexp/) {
                $matched = 1;
                print $sep if $matched2 && $nbef > $before && $show_sep
&& $show_fname;
                print "************** $file ****************\n" if !
$matched2++ && $show_fname;
                &clear_buf(1) if $before >0; $nbef = 0;
                &print_info(1); print $_;
               }
               elsif ($before > 0) {
                shift(_at_line_buf) if $nbef >= $before;
                push(_at_line_buf,$_); $nbef++;
               }
               }
        }
}
exit;

sub print_info {
        print _at__[0] ? "* " : " ";
        print $lnum," " if $show_nums;
}
sub clear_buf {
        $print_flag = _at__[0];
        $i = 0; $j = 0;
        if ($print_flag) {
               if ($show_nums) {
                $target = $lnum - ($#line_buf + 1);
                $lnum = "000000";
                while ($i++ < $target) { ++$lnum; }
               }
               while ($j <= $#line_buf) {
                &print_info(0);
                print _at_line_buf[$j++];
                $lnum++ if $show_nums;
               }
        }
        _at_line_buf = ();
}
sub usage {
# print STDERR _at__[0],"\en" if _at__[0];
        print STDERR "\nUsage: kgrep [-n] [-w[b]:[a] | -W] [-d] [-p] [-s]
[-m] regexp file(s)\n";
        print STDERR " -n = include line numbers\n";
        print STDERR " -s = indicate matched lines with
stars\n";
        print STDERR " -wb:a = show b lines before and a lines
after\n";
        print STDERR " ( default is 3 each)\n";
        print STDERR " -W = same as -w0:0 \n";
        print STDERR " -d = suppresses seperation lines
between file sections\n";
        print STDERR " -m = suppresses file name headers
lines\n";
        print STDERR " -p = same as -W -d\n";
        print STDERR " -h = prints this message\n";
        exit;
}



Here's the original message:

Hi all!

I've got an easy one for you this morning. I am wondering, how do you
window the output of a grep command to include the line before and/or
line after the matched string? I say "window" the output because that is
    

the name of the switch that you use in VMS along with the search command.
    



For example, if you had a file that looked like this:


Digital UNIX V4.0B (Rev. 564); Sun Feb 8 18:46:00 CST 1998
**************************************************************************
    

**
Missouri Employers Mutual Insurance
Creating a Healthy and Accident-Free Workplace.
**************************************************************************
    

**
Mon Mar 16 09:12:01 CST 1998


and you did a search for "employers" but you wanted to see the line
before and after the line that contained "employers" so that the output
    

would look like this:


**************************************************************************
    

**
Missouri Employers Mutual Insurance
Creating a Healthy and Accident-Free Workplace.


what command would you type in?

Thanks!




Stephen Spalding
sspaldin_at_mem-ins.com
Missouri Employers Mutual Insurance


Stephen Spalding
sspaldin_at_mem-ins.com
Missouri Employers Mutual Insurance
Received on Mon Mar 16 1998 - 18:23:43 NZST

This archive was generated by hypermail 2.4.0 : Wed Nov 08 2023 - 11:53:37 NZDT