hgrep Script
#!/usr/bin/perl -w
 
# hgrep.pl: "grep" with highlighting and context expansion (before and after lines)
#
# Usage: hgrep.pl [-HELP] [-iln] [-H] [-C (<n> | [<n>],[<m>])] <pattern> <files...>
#
#   -HELP          = Print help message.
#   -iln           = Same as "grep" options.
#   -H             = Turn on pattern highlighting.
#   -C <n>         = Displays <n> lines before and after the match.
#   -C [<n>],[<m>] = Displays <n> lines before and <m> lines after the match.
#                    If <n> or <m> is omitted, 0 is assumed for that number.
# 
# Note: If -HELP, -H, and -C are not specified, the arguments are passed on
#       to grep (including grep's normal options)
 
use strict;
 
# Global variables
my $me          = `basename $0`;
my $boldON      = "^[[01m";
my $boldOFF     = "^[[00m";
my $revON       = "^[[07m";
my $revOFF      = "^[[00m";
my $grepOption  = "-n";
my $pattern     = q//;
my $highlightON = 0;
my $contextON   = 0;
my $linesBefore = 0;
my $linesAfter  = 0;
my $shiftCount  = 0;
my $iFlag       = 0;
my $lFlag       = 0;
my $nFlag       = 0;
 
chomp ($me);
 
# If insufficient arguments, or "-HELP" specified, print help
 
if ( ($#ARGV < 1) or grep(/^-HELP$/, @ARGV) )
{
   PrintUsage();
}
 
ParseArguments();
 
my $firstFile = $shiftCount+1;
for my $fileArg ($firstFile..$#ARGV)
{
   grepFile ($ARGV[$fileArg]);
}
 
# End of main script
 
sub PrintUsage
{
   print "$me: "grep" with highlighting and context expansion (before and after lines)\n";
   print "\n";
   print "Usage: $me [-HELP] [-iln] [-H] [-C (<n> | [<n>],[<m>])] <pattern> <files...>\n";
   print "\n";
   print "  -HELP          = Print help message.\n";
   print "  -iln           = Same as "grep" options.\n";
   print "  -H             = Turn on pattern highlighting.\n";
   print "  -C <n>         = Displays <n> lines before and after the match.\n";
   print "  -C [<n>],[<m>] = Displays <n> lines before and <m> lines after the match.\n";
   print "                   If <n> or <m> is omitted, 0 is assumed for that number.\n";
   print "\n";
   print "Note: If -HELP, -H, and -C are not specified, the arguments are passed on\n";
   print "      to grep (including grep's normal options)\n";
 
   exit (0);
 
} # sub PrintUsage
 
# -----
 
sub ParseArguments
{
 
   # If "-H" specified, turn on highlighting
 
   if ( grep(/^-H$/, @ARGV) )
   {
      $highlightON = 1;
      $shiftCount++;
   }
 
   # If "-C" specified, turn on context expansion
 
   if ( grep(/^-C/, @ARGV) )
   {
      $contextON = 1;
      $shiftCount++;
   }
 
   # If "-HELP", "-H", and "-C" are NOT SPECIFIED, do a normal "grep"
 
   if ( !$highlightON and !$contextON )
   {
      exec 'grep', @ARGV
         or die "grep failed: $!\n";
   }
 
   # If we got here, we're doing a special grep.
 
   # We should check for grep's options
 
   if ( grep(/^-[iln]?[iln]?[iln]?$/, @ARGV) )
   {
      foreach ( grep(/^-[iln]?[iln]?[iln]?$/, @ARGV) )
      {
         $shiftCount++;
         if (/i/ and not $iFlag)
         {
            $iFlag = 1;
            $grepOption .= "i";
         }
         if (/l/)
            { $lFlag = 1; }
         if (/n/)      
            { $nFlag = 1; }
 
      } # foreach grep(/^-[iln]?[iln]?[iln]?$/, @ARGV)
 
   } # if ( grep(/^-[iln]?[iln]?[iln]?$/, @ARGV) )
 
   # If we are doing context expansion, find out how much
 
   if ( $contextON )
   {
      # First let's just get the range string
      my $rangeStr = q//;
      for my $i (0..$#ARGV)
      {
         # Are we at the "-C" arguement?
         if ($ARGV[$i] =~ /^-C/)
         {
            # Is the range string attached or separate?
            if ( $ARGV[$i] eq '-C' )
            {
               # Separate: Check the next argument
               if ( ($i < $#ARGV) and ($ARGV[$i+1] =~ /^\d*,?\d*$/) )
               {
                  # Extract the string
                  ($rangeStr) = ($ARGV[$i+1] =~ /^(\d*,?\d*)$/);
                  shiftCount++;
               }
            }
            else
            {
               # Attached!
               if ( $ARGV[$i] =~ /^-C\d*,?\d*$/ )
               {
                  # Extract the string
                  ($rangeStr) = ($ARGV[$i] =~ /^-C(\d*,?\d*)$/);
               }
            } # if ( $ARGV[$i] eq '-C' )
         } # if ($ARGV[$i] =~ /^-C/)
      } # for my $i (0..$#ARGV)
 
      # Now let's get the numbers
      if ($rangeStr)
      {
         if ($rangeStr =~ /^\d+$/ )
         {
            # Just "n"
            ($linesBefore) = ($rangeStr =~ /^(\d+)$/);
            $linesAfter = $linesBefore;
         }
         elsif ($rangeStr =~ /^\d+,$/ )
            { ($linesBefore) = ($rangeStr =~ /^(\d+),$/); }
         elsif ($rangeStr =~ /^,\d+$/ )
            { ($linesAfter) = ($rangeStr =~ /^,(\d+)$/); }
         else
            { ($linesBefore, $linesAfter) = ($rangeStr =~ /^(\d+),(\d+)$/); }
 
      } # if ($rangeStr)
 
   } # if ( $contextON )
 
   if ( scalar(@ARGV) <= ($shiftCount+1) )
   {
      # Not enough arguments: Print usage and exit
      PrintUsage();
   }
 
   $pattern = $ARGV[$shiftCount];
 
} # sub ParseArguments
 
# -----
 
sub grepFile
{
   # Input parameter is a filename
   my ($fname) = @_;
 
   # Store list of line numbers
   my @lineNums = ();
   my$lineno;
 
   # Build grep command
   my $grepCmd = "grep $grepOption \"$pattern\" $fname |";
 
   # Open a stream for the command to run in.
   open (GREPSTRM, $grepCmd)
      or die "Cannot perform $grepCmd\n$!\n";
 
   # Handle each line of output
   while (<GREPSTRM>)
   {
      #Clean up the line
      chomp;
 
      #Extract the line number
      ($lineno) = /^(\d+):/;
 
      #Add it to the list
      push @lineNums, $lineno;
 
   } # while (<GREPSTRM>)
 
   # Close the stream
   close (GREPSTREAM);
 
   # Did we find any matches in this file?
   if ( scalar(@lineNums) )
   {
      if ($lFlag)
         { print "$fname\n"; }
      else
         { print "$revON----- $fname -----$revOFF\n"; }
 
      # Read through the file again and print out the appropriate lines
      open (FSTRM, "< $fname")
         or die "Cannot open to read: $fname\n$!\n";
 
      my $lastMatchLine = 0;
      $lineno = 0;
      while (<FSTRM>)
      {
         # Clean up the line
         chomp;
 
         # Save the line
         my $currLine = $_;
 
         # Which line are we on?
         $lineno++;
 
         # Is it a matching line?
         if ( scalar(@lineNums) and ($lineno == $lineNums[0]) )
         {
            my $outputLine = "$currLine\n";
            if ( $highlightON )
            {
               # Highlight the pattern
               if ( $grepOption eq "-ni" )
               {
                  # Case insensitive
                  $outputLine =~ s/($pattern)/$boldON$1$boldOFF/gi;
               }
               else
               {
                  # Case sensitive
                  $outputLine =~ s/($pattern)/$boldON$1$boldOFF/g;
               }
            }
 
            # Prepending the line number?
            if ($nFlag)
               { $outputLine = $lineno . ':' . $outputLine; }
 
            if (not $lFlag)
               { print $outputLine; }
 
            # Get ready for the next matching line;
            $lastMatchLine = shift @lineNums;
         }
         elsif ( (scalar(@lineNums) and ($lineno >= ($lineNums[0]-$linesBefore)))
              or ($lastMatchLine and ($lineno <= ($lastMatchLine+$linesAfter))) )
         {
            # Context expansion line
            if ( $nFlag and not $lFlag)
               { print "$lineno:$currLine\n"; }
            elsif (not $lFlag)
               { print "$currLine\n"; }
         }
         elsif ( ($linesBefore or $linesAfter) and $lastMatchLine
             and ($lineno == ($lastMatchLine+$linesAfter+1)) and not $lFlag)
         {
            # Break up matches
            print "\n";
         }
 
      } # while (<FSTRM>)
 
      # Close the stream
      close (FSTRM);
 
   } # if ( scalar(@lineNums) )
 
} # sub grepFile
 
# -----
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License