1

I need a Sed/awk command to print the data between special charactes provided the pattern match should present in between special characters ({ & })

I have got a file which contains

define service {
host_name                       dns_vips
service_description             Multi Lookup 
use                             standard_service_template
active_checks_enabled           1
passive_checks_enabled          1
notifications_enabled           1
contact_groups                  mailgrp
max_check_attempts              3
normal_check_interval           5
retry_check_interval            1
notification_interval           10
check_period                    24x7
notification_period             24x7
notification_options            w,r,c
}
define service {
host_name                       dns_vips1
service_description             Multi Lookup 2  
use                             standard_service_template
active_checks_enabled           1
passive_checks_enabled          1
notifications_enabled           1
contact_groups                  mailgrp1
max_check_attempts              3
normal_check_interval           5
retry_check_interval            1
notification_interval           10
check_period                    24x7
notification_period             24x7
notification_options            w,r,c
}

I need data between { and } when the service description matches Multi Lookup

  • can you provide a sample of expected output ? – Archemar Oct 16 '15 at 9:11
  • So, in this example, you want both entries to be printed, right? Can you also have text that is not within { }? – terdon Oct 16 '15 at 10:38
3
sed '
    /{/{            #starts next code block if line include «{» 
        :1          #set mark point 
        /}/!{       #execute if pattern (collected lines) do not include «}»
            N       #add next line to the pattern space
            b1      #return to marked point
            }       #end of second (inner) block of code
        }           #end of first block of code 
    /Multi Lookup/p #prints pattern (collected lines) if it has «Multi Lookup»
    d               #clean pattern (start from the beginning)
' file
  • 2
    @harish If this answer solved your issue, please take a moment and accept it by clicking on the check mark to the left. That will mark the question as answered and is the way thanks are expressed on the Stack Exchange sites. – terdon Oct 16 '15 at 10:36
1

Just for fun I tried doing it as a vim one-liner. (Who ever heard of such a thing, right?)

vim -c 'g/service_description\s\+Multi Lookup\s\+$/?{?+,/}/-w! >> outputfile.txt' -c 'q!' inputfile.txt

What this does: Finds each line that contains service_description [whitespace] Multi Lookup [whitespace, end of line], and outputs all the lines from the preceding { to the following } for each match, not including the lines containing the { and } characters, and writing the output lines to outputfile.txt. It then exits without modifying inputfile.txt.

I couldn't tell if you wanted Multi Lookup 2 to match; if so, remove the \s\+$ after Multi Lookup.

If you wanted to include the lines with the curly brackets as well, remove the + after the ?{? and the - after the /}/.

Probably overkill since you can just use sed, but it was good practice for me. :)

  • could you code it in malbroge ? fish ? Ook ? – Archemar Oct 16 '15 at 11:24
1

This is fairly straightforward with e.g. perl if that's an option. Parse the record into key-value pairs, and then extract/match the field you want:

#!/usr/bin/env perl
use strict;
use warnings;

use Data::Dumper;

#set record separator to 'end bracket'. 
local $/ = '}';

#iterate our data based on that delimiter. 
#note - <> is a magic filehandle, in that it reads either pipe
#from stdin, or a file argument specified on command like (e.g. like awk/sed)
while (<>) {
    #extract key-value pairs with a multi line regex for this 'block'
    my %fields = m/(\w+)\s+(.*)$/gm;

    print Dumper \%fields; #to see what we captured for debugging. 

    #test a particular field against a regex. Note - this matches 
    #both in your example. 
    if ( $fields{service_description} =~ m/Multi Lookup/ ) {
        print "This record matches\n";
    }
}

Each record in the above gets put into fields, and is a perl hash containing:

$VAR1 = {
          'define' => 'service {',
          'use' => 'standard_service_template',
          'check_period' => '24x7',
          'host_name' => 'dns_vips',
          'service_description' => 'Multi Lookup ',
          'active_checks_enabled' => '1',
          'passive_checks_enabled' => '1',
          'notification_interval' => '10',
          'notification_period' => '24x7',
          'contact_groups' => 'mailgrp',
          'max_check_attempts' => '3',
          'notifications_enabled' => '1',
          'notification_options' => 'w,r,c',
          'normal_check_interval' => '5',
          'retry_check_interval' => '1'
        };

This could be compressed to a one liner if desired I'm sure, but you'd have to be a bit more specific about what you're actually wanting as output.

e.g.

 perl -ne 'BEGIN { $/ = "}" } %f = m/(\w+)\s+(.*)$/gm; print if $f{service_description} =~ m/Multi Lookup/'

Or perhaps an even simpler:

perl -ne 'BEGIN { $/ = "}" } print if m/service_description.*Multi Lookup/'
1

If I understand correctly, all of your data are inside { } and you want to print those records whose service_description matches Multi Lookup. If so, you can use a cool perl trick.

Perl has a "paragraph mode" where records (lines) are defined by blank lines. So, if you add a newline character after every }, you can simply do:

sed 's/}/}\n/' file | perl -00ne '/service_description\s*Multi Lookup/ && print'

The sed adds a \n after every }. Perl's -00 will turn on paragraph mode and -ne will cause it to read each input line (here, line means paragraph) and apply the script given by -e to it. The result is that records whose service_description matches Multi Lookup are printed.

Alternatively, you could set perl's record separator (what defines a "line"), the variable $/, in the script itself and avoid the sed step

perl -ne 'BEGIN{$/="}\n"}/service_description\s*Multi Lookup/ && print' file
  • Stuff like this is why I need to learn perl. Thanks for sharing. :) Correct me if I'm wrong, but won't this break if there happens to be an extra newline, say, after the {? – Wildcard Oct 16 '15 at 11:06
  • @Wildcard if there are two newlines after the { (one to end the line end then an empty one, then yes, it will. I am assuming (perhaps incorrectly) that this won't be the case. – terdon Oct 16 '15 at 11:12
  • Can also set $/ with a BEGIN block rather than needing to sed in front. – Sobrique Oct 16 '15 at 11:55
  • @Sobrique I find this both clearer and shorter than using a BEGIN block but how is that relevant? The $/ is set by the -00 flag, not by sed. The sed is just adding an extra newline so that can work. – terdon Oct 16 '15 at 11:57
  • Because then you don't need to use sed to insert a newline, you can $/ on } (Edited my suggestion accordingly). – Sobrique Oct 16 '15 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.