adonai has asked for the wisdom of the Perl Monks concerning the following question:

What would be the shortest way to grab the previous line from a file when you have a specfic search query. Like the following grab every instance of "foobar", but also grab the previous line.

Replies are listed 'Best First'.
Re: grabbing previous line in a log
by jmcnamara (Monsignor) on Aug 15, 2002 at 14:31 UTC

    This is a simple one-liner:     perl -ne 'print $prev, $_ if /foobar/; $prev = $_' file

    However, if there are several consecutive matches it will print duplicated data.

    This one doesn't produce duplicates:

    perl -ne 'BEGIN{$re=qr/foo/}if(/$re/){print$p if$p!~/$re/;print$_} +;$p=$_' file

    If you have a grep that supports it you can also do this:

        grep -B 1 foobar file

    And finally, this is a more generalised method of printing a matched line and the n-1 previous lines, with and without duplicates:

    perl -ne '$i=$.% 2; $a[$i]=$_; print @a[$i+1..$#a,0..$i] if /foo/' + file perl -ne '$i=$.% 2; $a[$i]=$_; print @a[$i+1..$#a,0..$i] and @a=() + if /foo/' file

    --
    John.

      Thanks works great!
Re: grabbing previous line in a log
by dimmesdale (Friar) on Aug 15, 2002 at 14:24 UTC
    Read through the file keeping track of two lines. I.e., have a $old_line and a $cur_line so that if $cur_line matches, $old_line should be the previous line.
Re: grabbing previous line in a log
by fruiture (Curate) on Aug 15, 2002 at 14:29 UTC

    Keep the previous line in a variable or at least keep its length or position (via tell()) in a variable to seek() back if you need:

    my $p; while(<FILE>){ #du stuff if( prevous line needed and defined $p ){ seek FILE, -(length()+$p), 1; my $prevous_line = <FILE>; # next <FILE> will return the current line again, # so call it again before going on! } } continue { $p = length() }
    --
    http://fruiture.de
Re: grabbing previous line in a log
by DamnDirtyApe (Curate) on Aug 15, 2002 at 18:32 UTC

    Here's a way to do it using Tie::File, which also gives you the option of grabbing the previous n lines, not just the line immediately previous (maybe more useful if you need context in a log file.)

    #! /usr/bin/perl use strict ; use warnings ; $|++ ; use Tie::File ; tie my @file, 'Tie::File', 'test_h.dat' or die "Can't tie file: $!" ; my $backtrack_lines = 1 ; my $match = '' ; for my $i ( 0 .. $#file ) { if ( $file[$i] =~ /baz/ ) { $match = join "\n", @file[ ( $i - $backtrack_lines ) .. $i ] ; } } print $match ; __END__

    _______________
    DamnDirtyApe
    Those who know that they are profound strive for clarity. Those who
    would like to seem profound to the crowd strive for obscurity.
                --Friedrich Nietzsche