#!/usr/bin/perl -w # # search - Search a file for text. Like grep, but prints a window. # Perl, Unix/Linux/Windows... # # 23-Jan-2006, ver 1.20 # # USAGE: search [-hinBI][-s num] pattern [files ...] # # -h # usage help # -i # ignore case # -n # print line numbers # -B # don't print borders # -I # don't invert match background # -s num # number of surrounding lines # eg, # search fred msg.txt # find "fred" in the file "msg.txt". # search -i fred msg.txt # case insensitive: "fred", "Fred"... # search -n fred msg.txt # print line numbers. # search -s3 fred msg.txt # print 3 surrounding lines. # search fred file1 file2 # find "fred" in multiple files. # cmd | search fred # search for "fred" in command output. # # Search is similiar to grep, however prints the surrounding two lines when # it finds matches. This is similar to the OpenVMS search command, or GNU's # grep with the window option. Regular Expressions are supported. # # The "pattern" is the search term. If regular expressions are used, they # are best kept in single forward quotes. # # Originally written in several lines, it was too difficult to follow. It # has now been rewritten in a verbose and conventional Perl style. # # SEE ALSO: GNU grep # # COPYRIGHT: Copyright (c) 2006 Brendan Gregg. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # (http://www.gnu.org/copyleft/gpl.html) # # 26-Jun-2003 Brendan Gregg Created this. # 23-Jan-2006 " " Tweaked style. use strict; use Getopt::Long; Getopt::Long::Configure ("bundling", "no_ignore_case"); # # Setup Variables # my @Files = (); # Files to print. my @Buffer = (); # Contains lines that may be printed. my $inv = `tput smso`; # Inverse ESC sequence. my $off = `tput rmso`; # Reset ESC sequence. my $state = "buffering"; # State of the main routine. my $WINDOW = 2; # Defaults, ... my $INVERSE = 1; my $NUMBERS = 0; my $BORDERS = 1; my $IGNORECASE = 0; my $linenum = 0; my $border = 0; my $print = 0; my $file = ""; # # Parse Options # my %Options = ( 'noborders' => sub { $BORDERS = 0; }, 'noinverse' => sub { $INVERSE = 0; }, 'help' => sub { usage(); }, 'ignorecase' => \$IGNORECASE, 'numbers' => \$NUMBERS, 'size' => \$WINDOW, ); GetOptions (\%Options, 'noborders|B', 'noinverse|I', 'help|h', 'ignorecase|i', 'numbers|n', 'size|s=i') or usage(); my $word = shift; usage() unless defined $word and $word ne ""; while (my $file = shift) { push @Files, $file; } # # MAIN # if (@Files > 0) { ### search files foreach $file (@Files) { open IN, $file or die "ERROR: Can't open file, $file: $!\n"; $linenum = 0; while (my $line = ) { search_line($line); } close IN; } } else { ### search STDIN while (my $line = ) { search_line($line); } } # search_line - searches a line for the input word. # # This is the main routine to search $line and print results. This is # a shortcut to a lump of code rather than a regular subroutine (hense # the usage of so many globals). # # Global flags used are $IGNORECASE, $NUMBERS, $BORDERS, $INVERSE, $WINDOW. # Global vars used are @Buffer, $state, $print, $linenum, @Files, $file. # # This is a stateful subroutine, which can be followed by the $state var. # This was set as a string for readability (not speed). # sub search_line { my $line = shift; my $junk; ### Check for a match if ($IGNORECASE) { $state = "found" if $line =~ /$word/i; } else { $state = "found" if $line =~ /$word/; } ### Add line numbers if ($NUMBERS) { $linenum++; $line =~ s/^/$linenum:/; } ### Add filename if (@Files > 1) { $line =~ s/^/$file:/; } ### Found, match found, print lines and buffer if ($state eq "found") { if ($border && $BORDERS && (- $print > $WINDOW)) { # The third term above solves a bug when sequential # windows shouldn't have a border. print "-----\n"; $border = 0; } if ($INVERSE) { if ($IGNORECASE) { $line =~ s/($word)/$inv$1$off/ig; } else { $line =~ s/($word)/$inv$1$off/g; } } print @Buffer, $line; @Buffer = (); $print = $WINDOW; # print more lines. $state = "printing"; } ### Printing, keep printing lines from a previous match elsif ($state eq "printing") { print $line if $print; $print--; if ($print <= 0) { # no more lines to print. $border = 1; # print border if needed. $state = "buffering"; } } ### Buffering, buffer lines in case of a future match elsif ($state eq "buffering") { push @Buffer, $line; $junk = shift @Buffer if @Buffer > $WINDOW; $print--; # counter for a border check. } } # usage - print the usage message and exit. # sub usage { print STDERR qq/USAGE: search [-hinBI][-s num] pattern [files ...] -h # usage help -i # ignore case -n # print line numbers -B # don't print borders -I # don't invert match background -s num # number of surrounding lines eg, search fred msg.txt # find "fred" in the file "msg.txt". search -i fred msg.txt # case insensitive: "fred", "Fred"... search -n fred msg.txt # print line numbers. search -s3 fred msg.txt # print 3 surrounding lines. search fred file1 file2 # find "fred" in multiple files. cmd | search fred # search for "fred" in output.\n/; exit 1; }