简体   繁体   中英

get line with second last occurrence of words in linux

I have two values "usb 1-3" and "address 20". Now I am trying to search a text file ( basically a log file) which has both of these words.

Now that file has many lines with these words as:

Apr 27 13:30:55 box2 kernel: usb 1-3: USB disconnect, address 20
and
Apr 27 13:25:43 box2 kernel: usb 1-3: new high speed USB device using ehci_hcd and address 20

Now my requirement is that: I want to get the line with the second-to-last occurrence of these two words and then next 16 lines after that. Like if there are 6 occurrences in file (6 lines with both of these words) then I need the line with the 5th occurrence and the next 16 lines after it. If there are 10 occurrences then I need the 9th occurrence and the next 16 lines.

currently I was using

egrep -A 20 'usb 1-3:.*address 20' filename | tail -16 > output.

but it gives me last occurrence. not second-to-last. please note that line should have Both Words and in same order (usb 1-3 first and address 20 later) Please tell me if my question isn't clear. Thanks in advance.

This requires two passes, but it should do the trick:

tail -n +`egrep -n 'usb 1-3:.*address 20' filename |
  tac | sed -n '2s/:.*//p'` filename | head -17

You could use awk or perl to do it in one pass, but you'd need to manage some buffers. Note that it uses tac , which is a reverse cat . If you don't have it on your system, try to drop tail -r in its place.

if you have GNU grep

grep -B 1 -A 15 'usb 1-3.*address 20' filename

http://manpages.debian.net/cgi-bin/man.cgi?query=grep

One way using perl :

use warnings;
use strict;

die qq[Usage: perl $0 <input-file>\n] unless @ARGV == 1;

my (@lines, $found);

## Reverse content and pipe output to perl.
open my $fh, q[-|], qq{tac $ARGV[0]} or die qq[Open error: $!\n];

## Read line by line.
while ( <$fh> ) { 

    ## Remove trailing '\n'.
    chomp;

    ## Use an array as a FIFO structure and save last 16 lines
    ## processed.
    if ( @lines > 16 ) { 
        pop @lines;
    }   
    unshift @lines, $_; 

    ## Count how many lines matches at same time 'usb 1-3' and
    ## 'address 20'.
    if ( m/usb\s1-3/ && m/address\s20/ ) { 
        ++$found;
    }   

    ## In second one, print lines saved.
    if ( ($found || 0) == 2 ) { 
        printf qq[%s\n], join qq[\n], @lines; 
    }   
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM