-
Notifications
You must be signed in to change notification settings - Fork 90
/
Copy pathg
executable file
·298 lines (252 loc) · 6.41 KB
/
g
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#!/usr/bin/perl
#
# TODO:
# - make -N work
# - support passing a directory to -R
# - long lines doesn't work with -v
# - make correct params
# - add documentation on each param
use System2;
=pod
g(rep++)
-samy kamkar
usage: g [files] [-ORz] [-N <file match>] [-i=file] [-e=file] [-options] <match> [-v !match] [-x <cmd, eg ls -lh>]
any normal options will be passed along to egrep
examples:
g the_match
-> greps * recursively for /the_match/
g some_file foo -v bar
-> greps some_file matching /foo/ while NOT matching /bar/
find . | g txt$ -x ls -lh
-> perform `ls -lh` on every file ending in 'txt'
g -N file_name_match
-> greps recursively for all file NAMES that match /file_match/
enabled by default: recursive, color, line numbers, tabs, ignore binaries, PCRE (perl regexpes), ignores .git/.svn
options:
-t until /pattern/
-g don't do maxlen
-R no recursion
-k prints everything but colorizes matches
-L line numbers
-x cmd (runs `cmd <matching line>` for every matching line)
-a (search binary files)
-O (output actual grep command)
-i (case insensitive)
-i=glob (search only files whose base name matches glob)
-e=glob (skip files who base name matches glob)
-v dont_match (ignore lines that match /dont_match/)
-N (find files that match this string and grep contents)
-z (gunzip files and grep them as well as non-gzipped files)
all other options will be passed along to egrep
=pod
# show lines in all files (in ., recursively) that match 'foo' but NOT 'bar'
g foo -v bar
# grep files/dirs a, b, and c for number.number (perl regexp \d\.\d)
g a b c '\d\.\d'
# perform `ls -lh` on every file ending in 'txt'
find . | g txt$ -x ls -lh
=cut
my $grep = '/opt/local/libexec/gnubin/grep'; # /opt/local/bin/grep
$|++;
use strict;
use Term::ANSIColor;
# XXX make this configurable
my $MAXLEN = 700; # max bytes to print in a line to avoid long lines from showing entirely
die "usage: $0 [files] [-gORz] [-N <file match>] [-i=file] [-e=file] [-options] <match> [-v !match] [-x <cmd, eg ls -lh>]\n" unless @ARGV;
#my $DEFAULTS = "-I";
my ($no, $noi, $stdin, $matchfiles, $binary, $color);
my $out = 0;
my $until;
my $piped = !-t STDIN;
if ($piped)
{
$binary = 1;
}
# if no piped in data (g ...), recursively crawl
else
{
$stdin = "r";
}
# stdout = true if piping out, eg g | something
my $stdout = -t STDOUT;
my $linenos = 0;
my @run;
#print STDERR "binary=$binary piped=$piped stdin=$stdin stdout=$stdout\n";
for (my $i = 0; $i < @ARGV; $i++)
{
#my $opt = $ARGV[$i];
if ($ARGV[$i] =~ /^-(\w+)$/)
{
# remove this option, readd below if needed
splice(@ARGV, $i, 1);
foreach my $opt (split //, $1)
{
$opt = "-$opt";
# show all text but colorize output
if ($opt eq "-k")
{
$color = 1;
}
# find files with this name
elsif ($opt eq "-N")
{
$matchfiles = splice(@ARGV, $i, 1);
}
# no maxln
elsif ($opt eq "-g")
{
$MAXLEN = 0;
}
# match until
elsif ($opt eq "-t")
{
$until = splice(@ARGV, $i, 1);
}
# no recursion
elsif ($opt eq "-R")
{
$stdin =~ s/r//;
}
# match binary files
elsif ($opt eq "-a")
{
$binary = 1;
}
# if running a program on each match
elsif ($opt eq "-x")
{
@run = splice(@ARGV, $i, @ARGV);
$stdout = 0;
}
# print grep output
elsif ($opt eq "-O")
{
$out++;
}
# else add back
else
{
splice(@ARGV, $i, 0, $opt);
}
}
}
}
if (@ARGV >= 3 && $ARGV[-3] !~ /^-/ && $ARGV[-2] =~ /^-(i)?v(i)?$/)
{
$noi = $1 || $2 ? "(?i)" : "";
$no = pop(@ARGV);
pop(@ARGV);
}
my $run;
for (my $i = 0; $i < @ARGV; $i++)
{
my $opt = $ARGV[$i];
$opt =~ s/^-?-i=/--include=/;
$opt =~ s/^-?-e=(.*)/--exclude=$1 --exclude-dir=$1/;
$opt =~ s/^-(\w*)e(\w*)$/-$1$2/;
$opt eq "-" ? splice(@ARGV, $i--, 1) : ($ARGV[$i] = $opt);
}
# grep features we want
splice(@ARGV, 0, 0, $grep, ($ARGV[0] =~ /^-/ || @ARGV == 1) && -t STDIN ? <*> : ());
# don't parse .git dirs
splice(@ARGV, -1, 0, "--exclude-dir=.git", "--exclude-dir=.svn", "-TP${stdin}");
# we want color
#splice(@ARGV, -1, 0, "--color=always");
splice(@ARGV, -1, 0, "--color=always") if $stdout;
# if we want line numbers
#splice(@ARGV, -1, 0, "-n") if $stdout && $linenos;
# don't process binary files unless we want to
splice(@ARGV, -1, 0, "-I") unless $binary;
# pattern follows
splice(@ARGV, -1, 0, "-e");
$ARGV[-1] .= '|$' if $color; # colorize output but show everything
print STDERR join(" ", @ARGV) . "\n" if $out;
if (length($no))
{
eval("use System2");
my ($out, $err) = system2(@ARGV);
my @out = split("\n", $out);
foreach my $line (@out)
{
(my $tmp = $line) =~ s/\e\[(?:\d+(?:;\d+)?)?m|\e\[K//g;
if (@run)
{
run(@run, $line);
}
else
{
print "$line\n" if $tmp !~ /$noi$no/i;
}
}
print STDERR $err;
}
else
{
if (@run)
{
my @out = map { chomp; $_ } `@ARGV`;
run(@run, @out);
#map { chomp; system(@run, $_) } `@ARGV`;
}
else
{
run(@ARGV);
}
}
# handle long lines
sub run
{
# TODO: add support for handling oversized piped data
if ($piped)
{
system(@_);
return;
}
my ($cmd, @args) = @_;
my $match = $args[-1];
open(GREP, "-|", $cmd, @args) || die "Can't run `$cmd @args`: $!";
# check for case insensitivity
$match = "(?:(?i)$match)" if grep { $_ eq "-i" } @args;
# grab 3 extra bytes of ansicolor after :
my $colorbytes = $stdout ? ".{7}" : "";
#print "cb=$colorbytes\n";
while (<GREP>)
{
my $d = $_;
#print "d=$d\n";
# XXX add option to disable this cutting
if ($MAXLEN && length($d) > $MAXLEN)
{
my $nl = 0;
# grab/print file name
$d =~ s/^(.*?.:$colorbytes)//;
print $1;
#print " cb=$colorbytes 1=$1\n";
while ($d =~ s/^(.*?)(.{0,$MAXLEN})($match)(.{0,$MAXLEN})//s)
{
my ($extra, $pre, $m, $post) = ($1, $2, $3, $4);
#print "e=$extra p=$pre m=$m($match) P=$post\n";
print colored("<...>", 'cyan') if $extra;
#print "(EXTRA=$extra)";
print $pre;
print $m;
if ($post =~ /$match/)
{
$d = $post . $d;
pos($d) = 0;
}
else
{
print $post;
}
$nl++ if (($pre . $m . $post) =~ /\n/);
}
print colored("<...>", 'cyan') if $d;
print "\n" if !$nl;
}
else
{
print;
}
}
}