Print entire line of key=value pairs if field in line meets condition - awk

Viewed 66

I have logs with data containing key=value pairs separated by space.

time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=100.00

I want to print the entire line, where the SuccessRate is < 99.

So far I've used awk to figure out where the SuccessRate is < 99, but it only prints the field itself, not the entire line:

awk '/SuccessRate/ {print $NF}' file | awk -F "=" '$2<=99{print ;}'

Doing this prints out :

SuccessRate=98.64
SuccessRate=0.00  
SuccessRate=16.09
...

However, I want to print the entire line when the condition is met:

time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=98.64
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=0.00
...

Not sure how to do it without the extra pipe. Additionally, how would I do this if the field I was interested in was not the last field in the line? i.e. what if I wanted to print if TxPkts met a condition?

7 Answers

You may use match function in awk:

awk -v s='SuccessRate=' 'match($0, s "[.[0-9]+") && substr($0,RSTART+length(s))+0 <= 99' file

time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=98.64
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=0.00

If the SuccessRate could be in any field:

$ awk '{
    for(i=1;i<=NF;i++) {                      # iterate all field
        split($i,a,/=/)                       # split on =
        if(a[1]=="SuccessRate" && a[2]<99) {  # on success
            print                             # output and
            next                              # next line please
        }
    }
}' file 

If it is always in the last field:

$ awk '{
    split($NF,a,/=/)                          # split and check last field only
    if(a[1]=="SuccessRate"&&a[2]<99)
        print
}' file

You never want to make two calls to awk with a pipe in between when a single call to awk will do (which is virtually every time). In your case you can simply save the last field and the use sub to remove SuccessRate= leaving the value which you can compare against 99, e.g.

awk '{f=$NF; sub(/SuccessRate=/,"",f); if (f+0<99) print}'

With your input in pairs.txt, you would receive no output from:

$ awk '{f=$NF; sub(/SuccessRate=/,"",f); if (f+0<99) print}' pairs.txt

but would receive output for any value where SuccessRate < 99. The f+0 ensures f is treated as a number and not a string.

With your shown samples only. Please try following awk program. Written and tested in GNU awk version of it.

awk -v RS=' SuccessRate=[0-9]+(\\.[0-9]+)?\\n' '
prev && RT && split(RT,arr,"=") && arr[2]<99{
  print prev
}
NF{ prev=$0 }
'  Input_file

Setup:

$ cat file
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=98.64
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=19 DroppedPkts=0 FailedRetries=0 SuccessRate=198.64
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=209 DroppedPkts=0 FailedRetries=0 SuccessRate=0.00
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=399 DroppedPkts=0 FailedRetries=0 SuccessRate=398.64

One awk idea:

attrib='SuccessRate'

awk -v attrib="${attrib}" '
{ for (i=1;i<=NF;i++) {                     # loop through attrib=value pairs
      split($i,a,"=")                       # split on "="; a[1]=attrib a[2]=value
      if (a[1]==attrib && a[2] < 99) {      # if we have a match then ...
         print                              # print current line and ...
         next                               # skip to next line of input
      }
  }
}
' file

For attrib='SuccessRate' this generates:

time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=98.64
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=209 DroppedPkts=0 FailedRetries=0 SuccessRate=0.00

For attrib='TxPkts' this generates:

time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=98.64
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=19 DroppedPkts=0 FailedRetries=0 SuccessRate=198.64

OP could modify this further to include parameterizing the 99 (eg, -v limit='99' and if (a[1]==attirb && a[2]<limit)); parameterizing the comparison operator (< in this case) could be done with a bit more work.

Using awk

$ awk -F" |=" '{for(i=1;i<NF;i++) if ($i=="SuccessRate" && $(i+1)<99) print}' input_file
time=2022-08-10T16:03:23Z Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=98.64
time=2022-08-10T16:03:23Z Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=0.00

Whenever there's key=value pairs in the input as you have, I find it best to create an array, f[] below, holding that mapping and then you can access the values by their keys (names) in whatever order you like, for testing, printing, modifying, etc. e.g.:

awk '
    {
        for ( i=1; i<=NF; i++ ) {
            split($i,keyval,"=")
            f[keyval[1]] = keyval[2]
        }
    }
    f["SuccessRate"] < 99
' file
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=98.64
time="2022-08-10T16:03:23Z" Event=xyz TxPkts=9 DroppedPkts=0 FailedRetries=0 SuccessRate=0.00
Related