I’ve spoken about processing logfiles with perl previously. Occasionally though, I still reach for sed.
Say I have a logfile that looks like this:
[ <timestamp> ] : somefunc() [ <timestamp> ] : interesting line 1 [ <timestamp> ] : interesting line 2 ... 1000s of lines [ <timestamp> ] : somefunc() [ <timestamp> ] : interesting line 1 [ <timestamp> ] : interesting line 2 ... 1000s of lines
Picking out lines following a pattern is easy with sed – p prints the current match and n takes the next line.
$ < log.txt sed -n '/somefunc()/ {p;n;p;n;p}'
[ <timestamp> ] : somefunc()
[ <timestamp> ] : interesting line 1
[ <timestamp> ] : interesting line 2
[ <timestamp> ] : somefunc()
[ <timestamp> ] : interesting line 1
[ <timestamp> ] : interesting line 2
My first attempt to replace that with perl looks a bit ugly
< log.txt \
perl -ne 'if (/somefunc\(\)/) {print; print scalar(<>) for (1..2)}'
I’m not that happy with the module I came up with to hide the messiness either.
package Logfiles; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(process); use Carp; sub process (&$;$) { my ($sub, $regex, $lines) = @_; $lines ||= 0; return unless /$regex/; if (! $lines) { $sub->($_); } else { croak "process() arg 3 not ref ARRAY" unless ref($lines) eq 'ARRAY'; my $line = 0; my @lines = @$lines; while (1) { if ($lines[0] == $line) { $sub->($_); shift @lines; } ++$line; last if ($line > $lines[0] or (not @lines)); $_ = <>; } } } 1;
But at least my typical spelunking looks a little cleaner now.
< log.txt perl -MLogfiles=process -ne \
'process { print } qr/somefunc\(\)/, [0..2]'
Any suggestions on how to improve this (without reverting to sed)?