3 # Programname: fpg - Frams' Perl grep
4 # Author: framstag@rus.uni-stuttgart.de
5 # Licence: Perl Artistic
8 # 2003-02-27 Framstag initial version
9 # 2003-02-28 Framstag added exit status
10 # 2007-03-09 Framstag added option -Q
11 # 2007-06-01 Framstag added options -s and -c
12 # and changed default output mode
13 # 2007-06-03 Framstag added ReadLine-support
14 # 2007-08-31 Framstag added option -x
15 # 2008-02-06 Framstag added implicit gunzip
16 # -F ==> -R, new -F option
17 # 2008-10-07 Framstag added option -p
18 # -n ==> -S, new -n option
19 # 2008-10-14 Framstag added option -M
20 # 2008-11-23 Framstag added option -~
21 # 2016-06-12 Framstag option -o respects (match)
31 usage: $0 [options] 'EXP' [file...]
32 or: $0 [options] -Q file...
33 options: -r recursively scan through directories
35 -v print only lines that do NOT match
36 -s verbose scanning/searching
37 -n prefix with line number
38 -l list filenames only
39 -L list filenames only that do NOT match
40 -p show paragraphs, not lines (multiline record separator)
41 -o show only matched strings (in parenthesis), not whole lines
42 -M mail-mode: search and show complete mails from mbox files
43 -c print (count) only number of matches (NOT LINES!)
44 -F EXP is a string, not a Perl regular expression
45 -e EXP is any perl code which returns TRUE/FALSE
46 -S \# minimum string length \# for binary files, default: 4
47 -C \# \# lines of context
48 -R 'RS' record separator, default: newline (\\n) if not set -p
49 -x 'exp' extra regexp for highlighting (not used for searching)
50 -X 'exp' exclude files (filename matching this regexp) when searching
51 -~ search in backup files *~ #*#, too
52 -Q query-loop-prompt for search expression (with readline)
53 arguments: EXP is a Perl regular expression
54 file... can be one or more files, even binary or compressed ones
56 #examples: $0 -r 'from.*STDIN' *
57 # $0 -e 'length>30 and not /\\w/' script
58 #See "perldoc perlre" for help on regular expressions.
63 $opt_i = $opt_r = $opt_v = $opt_l = $opt_h = $opt_e = $opt_n = $opt_o = 0;
64 $opt_s = $opt_c = $opt_Q = $opt_F = $opt_p = $opt_M = $opt_C = $opt_S = 0;
70 getopts('hirvlLFMopscQen~S:R:C:x:X:') or die $usage;
78 $exp = shift or die $usage;
81 if ($opt_C and ($opt_l or $opt_L or $opt_s or $opt_v or $opt_p or $opt_M)) {
82 die "$0: cannot mix option -C with any of -l -L -s -v -p -M\n";
85 if ($opt_M and ($opt_l or $opt_L or $opt_s or $opt_v or $opt_p or $opt_C)) {
86 die "$0: cannot mix option -M with any of -l -L -s -v -p -C\n";
89 if ($opt_o and ($opt_v or $opt_l or $opt_L or $opt_c or $opt_F or $opt_C)) {
90 die "$0: cannot mix option -E with any of -l -L -v -c -C -F\n";
95 @bfiles = grep(/~$|^#.*#$/,@ARGV);
97 (grep(/[^~]$/,@ARGV) or grep(/(^|\/)#[^\/]*#$/,@ARGV))) {
99 warn "$0: ignoring @bfiles\n"; # unless $opt_r;
110 if ($opt_p) { $/ = '' }
112 #else { eval '$/ = "'.$opt_R.'"' }
114 $opt_h = 1 if not $opt_r and @ARGV < 2;
117 $q = new Term::ReadLine $0;
118 $q->ornaments(0) unless $ENV{PERL_RL};
120 $exp = $q->readline("$B\nsearch-expression:$N ");
133 eval "\$egrep = sub { $exp }";
135 $exp =~ s/([\@\$\%\^\&\*\(\)\+\[\]\{\}\\\|\.\?])/\\$1/g if $opt_F;
136 $exp = '(?i)'.$exp if $opt_i;
137 $exp = '(?s)'.$exp if $opt_p or $opt_R ne "\n";
138 #? $exp =~ s/\.\*\*/[.\n]*/g;
144 foreach $file (@ARGV) {
145 next if $opt_X and $file =~ /$opt_X/;
146 next if $opt_XX and ($file =~ /~$/ or $file =~ m{(^|/)#[^/]*#$});
147 my $error = ''; open $file,$file or $error = $!; close $file;
149 warn "$0: cannot read file $file - $error\n";
152 unless (-f $file or -d $file or -c $file or -S $file or -p $file) {
153 warn "$0: ignoring special file $file\n";
156 $maxlen = length $file if $maxlen < length $file;
157 # printf "%s\r",substr("scanning $file".(" " x 255),0,$maxlen+9) if -t STDOUT;
158 # print $B."scanning $file\n".$N if -t STDOUT and not $opt_l||$opt_L;
159 if ($opt_r and -d $file) {
160 $found += grepd($file);
163 # next if -z $file; # Achtung: special files unter /proc sind "empty" !
164 # $type = `file -L $file`;
165 # if ($type =~ /text/i and open F,$file or open F,"strings $file|") {
166 $fileq = quotemeta $file;
171 if ($file =~ /\.bz2$/) {
172 open $file,"bunzip2 <$fileq|";
173 # warn "gunzip <$file|\n";
174 } elsif ($file =~ /\.gz$/) {
175 open $file,"gunzip <$fileq|";
176 # warn "gunzip <$file|\n";
178 open $file,"strings -a -n $opt_S $fileq|";
179 # warn "strings -n $opt_S $file|\n";
183 $found += grepf($file,$file);
186 warn "$0: cannot open $file - $!\n";
190 # print " " x ($maxlen+9),"\r" if -t STDOUT;
192 $found = grepf(STDIN);
201 opendir $dir,$dir or return;
202 while (defined($file = readdir $dir)) {
203 next if $file eq '.' or $file eq '..';
204 if (not ${'opt_~'} and $file =~ /~$|^#[^\/]*#$/) {
205 # warn "$0: ignoring $dir/$file\n";
208 $file = "$dir/$file";
209 next unless -r $file;
210 if (-d $file and not -l $file) {
211 $found += grepd($file);
214 next unless -f $file or -c $file or -S $file or -p $file or -z $file;
215 $fileq = quotemeta $file;
216 if (-T $file and open $file,$file or
217 open $file,"strings -a -n $opt_S $fileq|") {
218 $found += grepf($file,$file);
233 warn $B."scanning $file".$N."\n" if -t STDOUT and $opt_s;
236 $_ .= "\n" unless /\n$/;
238 if ($mail and (/^From / or eof $F)) {
250 for (my $i=$opt_C;$i;$i--) {
251 $C{$i} = $C{$i-1} if defined $C{$i-1};
261 print "$l:" if $opt_n;
272 # print ">>>$_" if $opt_i and /$exp/oi or /$exp/o;
281 if ($opt_Q) { $n++ while /$exp/mg }
282 else { $n++ while /$exp/omg }
285 if ($exp =~ /\([^?]+\)/) {
299 $n += s/($exp)/$B$1$N/mg;
301 $n += s/($exp)/$B$1$N/omg;
308 print "$l:" if $opt_n;
316 # print " " x ($maxlen+9),"\r" if -t STDOUT and $found==1;
318 last if $opt_l or $opt_L;
319 if ($file and not $opt_s) {
320 print "\n$B$file$N:\n";
324 if ($opt_i) { s/($opt_x)/$B$1$N/ogi }
325 else { s/($opt_x)/$B$1$N/og }
327 for (my $i=$opt_C;$i;$i--) {
328 if (defined $C{$i}) {
329 my ($ln,$ls) = @{$C{$i}};
330 unless (defined $L{$ln}) {
332 print "$ln:" if $opt_n;
337 print "$l:" if $opt_n;
344 print "$file:" if @ARGV>1;
347 print "$file\n" if $opt_l and $found or $opt_L and not $found;