Awk script returns nothing

HSLIST=$1

LFILE=$2

STRING=$3

awk 'BEGIN {
  while((getline < "'${HSLIST}'")>0)
     S[$0]

  FS="\n"; RS="}\n"
}

/servicestatus {/ && /service_description='${STRING}'/ {

  for(X in D) delete D[X];

  for(N=2; N<=NF; N++)
  {
       split($N, A, "=");
       D[A[1]] = A[2]
       i = 3;
       while (i in A) 
          D[A[1]] = D[A[1]] "=" A[i++];
  }

  if (D[" host_name"] in S)
       printf("%s -------- %s -------- %s -------- %s -------- %s -------- %s -------- %s -------- %s\n", D["host_name"], D["service_description"], D["check_execution_time"], D["check_latency"], D["last_check"], D["last_hard_state_change"], D["current_state"], D["plugin_output"])

}' $LFILE

Can someone please explain to me why the above code isn't pulling out anything from a file that contain several chunks like the one below?

when i run the above code, i get back nothing.

synopsis:

i have a file that contains several of the chunks below. the chunks belong to several servers. one server may have several chunks associated with it.

in the below example, this specific chunk belongs to a server called "sky-01.net". there may be other chunks like this in a file, with service description "CPU_CHECK", "DISK_CHECK", etc.

servicestatus {
        host_name=sky-01.net
        service_description=LOAD_CHECK
        modified_attributes=1
        check_command=check_load!68!90
        check_period=24x7
        notification_period=24x7
        check_interval=30.000000
        retry_interval=2.000000
        event_handler=
        has_been_checked=1
        should_be_scheduled=1
        check_execution_time=1.362
        check_latency=7.887
        check_type=0
        current_state=0
        last_hard_state=0
        last_event_id=2523376
        current_event_id=2611954
        current_problem_id=0
        last_problem_id=1127556
        current_attempt=1
        max_attempts=1
        state_type=1
        last_state_change=1337124526
        last_hard_state_change=1337124526
        last_time_ok=1337433055
        last_time_warning=0
        last_time_unknown=1335465427
        last_time_critical=1337123063
        plugin_output=INFORMATIONAL:  Check is ok. NOthing to worry about.
        long_plugin_output=
        performance_data=
        last_check=1337433055
        next_check=1337434855
        check_options=0
        current_notification_number=0
        current_notification_id=2699849
        last_notification=0
        next_notification=0
        no_more_notifications=0
        notifications_enabled=1
        active_checks_enabled=1
        passive_checks_enabled=1
        event_handler_enabled=1
        problem_has_been_acknowledged=0
        acknowledgement_type=0
        flap_detection_enabled=0
        failure_prediction_enabled=1
        process_performance_data=1
        obsess_over_service=1
        last_update=1337433118
        is_flapping=0
        percent_state_change=0.00
        scheduled_downtime_depth=0
        }

It's hard to tell as you are missing to provide some clues to understand your code, its parameters and more information about its input file, however I would probably insert

gsub(/^[ \t]+/, "", $N);

just before

split($N, A, "=");
1 Like

And probably need to remove the whitespace in the string:

if (D[" host_name"] in S)

so that it is

if (D["host_name"] in S)
1 Like

jillagre and agama's suggestions both worked.

can someone please explain the code to me in its totality?

the script runs like this:

code.sh  /var/tmp/listofhosts.txt  /usr/chunk.cfg LOAD_CHECK

Here is your code with some comments to describe what is going on:

awk 'BEGIN {
  while((getline < "'${HSLIST}'")>0)    # read all names from the host list
     S[$0]                              # create an instance in the S hash; ="" is implied

  # file should have been closed with close( filename )

  FS="\n"; RS="}\n"                     # after reading host list; change field and record separators
                                        # this effectively causes each block to be one input record
}

/servicestatus {/ && /service_description='${STRING}'/ {    # execute the following code when the input record
                                                            # contains "servicestatus (" and "service_description="
  for(X in D) delete D[X];                                  # delete previous values in D;  delete( D ) is more efficient

  for(N=2; N<=NF; N++)                                      # for each field, starting with 2, (these are name=value pairs)
  {
       split($N, A, "=");                                   # divide the field int array A.  a[1] contains name, a[2] contains the value
       D[A[1]] = A[2]                                       # create an instance in the D hash using the name to map to value
       i = 3;                                               # not in your example input, but this handles the case where the
       while (i in A)                                       # name=value is actually  name=value=more=stuff
          D[A[1]] = D[A[1]] "=" A[i++];                     # by adding more=stuff to the value saved in D[name]
  }

  if (D[" host_name"] in S)                                 # if the hostname sussed from the data was in the file read; print the info
       printf("%s -------- %s -------- %s -------- %s -------- %s -------- %s -------- %s -------- %s\n", D["host_name"], 
   D["service_description"], 
   D["check_execution_time"], 
   D["check_latency"], 
   D["last_check"], 
   D["last_hard_state_change"], 
   D["current_state"],
   D["plugin_output"])

}' $LFILE

There are a couple of things that should be changed: close the host list file after reading it; that is just good practice. The for loop to delete the values in D isn't needed. You can just delete( D ) or use split to delete the hash more efficiently. Lastly, I don't like the quoting games used to incorporate shell variables. It's messy, and leads to bugs during maintenance.

I also don't think it's necessary to play games with the record/field separators; at least not in this case. Just for fun, here's an example that brings in shell variables without quoting games, and doesn't need to play with the separators:

awk -v hostf=$HSLIST -v desc=$STRING '
    BEGIN {
        while( (getline < hostf) > 0 )
            hlist[$1];
        close( hostf );                         # good practice to close a file when done with it

        split( "host_name service_description check_execution_time check_latency last_check last_hard_state_change current_state plugin_output", desired, " " );
    }

    /servicestatus {/ { snarf = 1; next; }      # beginning of desired chunk -- start saving info

    /}/ && snarf {
        if( data["host_name"] in hlist )                    # if host was in the list; print its data
        {
            for( i = 1; i <= length( desired ); i++ )       # print desired output; less efficient, but more flexible when adding or changing order
                printf( "%s%s", i > 1 ? " ------- " : "", data[desired] );
            printf( "\n" );
        }
        delete( data );                     # use split( "", data, "." );  if awk version does not support delete()
        snarf = 0;
        next;
    }

    snarf {                                 # saving data; cache value (all after first =) in data hash
        nm = $1;
        gsub( "=.*", "", nm );              # drop everything after first = from name string
        val = $0;
        sub( "^[^=]*=", "", val );          # drop name= from the value string
        data[nm] = val;                     # save it
    }
' input-file
1 Like