2

Let's say I have a well-structured text file with following content(no leading line numbers):

 1 Mon Jun 9 00:11:47 CST 2014
 2 eth0      Link encap:Ethernet  HWaddr D4:BE:D9:F5:5C:0E
 3           inet addr:10.179.113.125  Bcast:10.179.113.127  Mask:255.255.255.248
 4           inet6 addr: fe80::d6be:d9ff:fef5:5c0e/64 Scope:Link
 5           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
 6           RX packets:1169385 errors:0 dropped:0 overruns:0 frame:0
 7           TX packets:250825 errors:0 dropped:0 overruns:0 carrier:0
 8           collisions:0 txqueuelen:10000
 9           RX bytes:365792552 (348.8 MiB)  TX bytes:20648578 (19.6 MiB)
10           Interrupt:24 Memory:d6000000-d6012100
11 Tue Jun 10 05:11:47 CST 2014
12 eth1      Link encap:Ethernet  HWaddr D4:BE:D9:F5:5C:10
13           inet addr:10.254.4.1  Bcast:10.254.4.255  Mask:255.255.255.0
14           inet6 addr: fe80::d6be:d9ff:fef5:5c10/64 Scope:Link
15           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
16           RX packets:3806158038 errors:0 dropped:23193484 overruns:0 frame:0
17           TX packets:1206000723 errors:0 dropped:0 overruns:0 carrier:0
18           collisions:0 txqueuelen:10000
19           RX bytes:1596108082 (1.4 GiB)  TX bytes:2960952707 (2.7 GiB)
20           Interrupt:25 Memory:d8000000-d8012100

Now what I need is to filter out line#1, #3, #6 for the eth0 section and line #11, #13, #16 for the eth1 section with the grep keyword 'eth'.

Mon Jun 9 00:11:47 CST 2014    
inet addr:10.179.113.125  Bcast:10.179.113.127  Mask:255.255.255.248
RX packets:1169385 errors:0 dropped:0 overruns:0 frame:0
Tue Jun 10 05:11:47 CST 2014
inet addr:10.254.4.1  Bcast:10.254.4.255  Mask:255.255.255.0
RX packets:3806158038 errors:0 dropped:23193484 overruns:0 frame:0

How can i make this?

1
  • 1
    When you say "filter out". Do you mean keep, and filter out everything else? Commented Jun 10, 2014 at 11:33

5 Answers 5

2

This worked for me:

sed -n '/eth/{n;p;n;n;n;p;}' file
  • Search for the string eth
  • n;: skip a line, p;: print the line
  • n;n;n;: skip 3 lines and print again

Edit:

This prints the line above, the line after and the line after 3 lines:

sed -n -e '/eth/{x;1!p;g;$!n;p;n;n;n;p;D;}' -e h file

And your second question: I think you may not come around typing 20 times the n; command...

4
  • Thanks. This is exactly what I need. Your command can print the specific lines below the matching line. Now I'm wondering how can I print the lines above the matching line?
    – snowfox
    Commented Jun 10, 2014 at 7:39
  • and one more question..what if I need to print the 20th line following the matching line. Do I have to type the 'n' 20 times in the command?
    – snowfox
    Commented Jun 10, 2014 at 7:44
  • @user1087373 ask the above comment as another question or edit your question to satisfy your need. Commented Jun 10, 2014 at 7:47
  • @user1087373 see my edit
    – chaos
    Commented Jun 10, 2014 at 9:06
1

Try this awk command,

$ awk '$1~/^eth/ {getline; print; getline; getline; getline; print}' file
          inet addr:10.179.113.125  Bcast:10.179.113.127  Mask:255.255.255.248
          RX packets:1169385 errors:0 dropped:0 overruns:0 frame:0
          inet addr:10.254.4.1  Bcast:10.254.4.255  Mask:255.255.255.0
          RX packets:3806158038 errors:0 dropped:23193484 overruns:0 frame:0

Update:

$ awk '$1~/^eth/ {print previous; getline; print; getline; getline; getline; print}{previous=$0}' file
Mon Jun 9 00:11:47 CST 2014
           inet addr:10.179.113.125  Bcast:10.179.113.127  Mask:255.255.255.248
           RX packets:1169385 errors:0 dropped:0 overruns:0 frame:0
Tue Jun 10 05:11:47 CST 2014
           inet addr:10.254.4.1  Bcast:10.254.4.255  Mask:255.255.255.0
           RX packets:3806158038 errors:0 dropped:23193484 overruns:0 frame:0
1

Here's a different approach that uses Perl and gives you more flexibility for which lines you want to print:

perl -n00E '
    @eth_records= grep {/eth/} split/(?=>Mon|Tue|Wed|Thu|Fri|Sat|Sun)/;
    @lines_to_print = qw{1 3 6};
    map { $_-- } @lines_to_print;
    $sep = "-"x80;
    for(@eth_records){
        say $sep;
        say for (split/\n/)[ @lines_to_print ];
        say $sep
    }' your_file

This splits the file into records at lines starting with a day's name and prints the 1st 3rd and 6th lines of all records matching /eth/

Note that it will load your whole file into memory so avoid it if your file is large.

0

There are several ways. Please have a look at following questions on SO:

4
  • 2
    Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.
    – slm
    Commented Jun 10, 2014 at 7:39
  • Thx for the info. These 2 posts are pretty good. But they don't solve my question fully
    – snowfox
    Commented Jun 10, 2014 at 7:59
  • Easy way to use is grep command i suppose.Using head and tail its very easy.Say we want 4th line after the pattern matched line grep -A 4 eth FileName| tail -1 Please note that this works only for BSD or GNU grep
    – Tingrammer
    Commented Jun 10, 2014 at 10:12
  • One more solution is to use bash scripting. Getting the line numbers to a variable, then printing using sed.If the number of occurrence is more then according it should be programmed. num=$( grep -c eth fileName | head -1 ); sed -n '${num}p' fileName If the number of occurrence is more then, put these line numbers in array and print these lines using sed one after the other.
    – Tingrammer
    Commented Jun 10, 2014 at 10:22
0

Here are a few suggestions:

  • grep

    grep -P 'CST|inet |RX p' file
    

    This will print all lines that contain CST or inet followed by a space or RX pa. The -P activates Perl Compatible Regular Expressions which lets us use | as a logical OR. You could also achieve the same thing with either of

    grep -E 'CST|inet |RX p' file
    

    or

    grep  'CST\|inet \|RX p' file
    
  • sed

    sed -n '/CST\|inet \|RX p/p' file
    sed -rn '/CST|inet |RX p/p' file
    

    Same idea as above, the -n suppresses printing of any lines and the //p means print those lines that matched the pattern.

  • perl

    You can use the same approach in Perl:

     perl -ne 'print if /CST|inet |RX p/' file
    

    Or you could do something like

     perl -ne '$k=1 if /CST/; print if $k==1||$k==3||$k==6; $k++' file
    

    Here, the variable $k is set to 1 if the line matches CST and is incremented by one after each line is read. The line is then printed if $k has a value of 1,3 or 6. This is a more extensible approach.

    An alternative, assuming you know the line numbers you want to print, would be to simply print them directly ($. is the current line number):

    perl -ne 'print if $.==1||$.==3||$.==6||$.==11||$.==13||$.==16' file
    

    or, more idiomatically:

    perl -ne '@d=(1,3,6,11,13,16); print if $.~~@d' file
    

    Finally, you can also load the entire file into memory and print only the lines you care about:

    perl -e '@F=<>; print @F[0,2,5,10,12,15]' file
    
  • awk

    You can use the same basic approaches in awk where NR is the current line number:

    awk '/CST|inet |RX p/' file
    

    or

    awk '{if(/CST/){k=1} if(k==1||k==3||k==6){print} k++;}' file
    

    or

    awk 'NR==1||NR==3||NR==6||NR==11||NR==13||NR==16' file
    

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .