pdxwarrior
pdxwarrior

Reputation: 23

Compare values within line of file, display line if condition met

Trying to sort out an issue here. I have a file that includes many lines, but I want to filter for specific line, then compare two values within that line, and if the condition is met, I want to display said line. This would be done through the entire file. I'm looking to use a BASH script for this.

Format of line:

<timestamp>  <date> : <ServerName> <Device> <In>/<Out> <Value1>/<Value2>

Example of line:

15:13:33   12/13/19 : Host1  Device1  In/Out 33/34

Using this, I want to do the following:

So far, I have:

#!/bin/bash

input="logfile.log"

while IFS= read -r line
do
     echo $line
done < "$input"

I understand where the echo $line is, I could perform commands. But I'm not sure how to "echo" this and process it within the while loop.

I could do something like:

line=`grep "In/Out" logfile.log`
var1=`grep "In/Out" logfile.log | awk -F" " '{ print $7 }' | awk -F/ '{ print $1 }'`
var2=`grep "In/Out" logfile.log | awk -F" " '{ print $7 }' | awk -F/ '{ print $2 }'`

And then compare, the difference, and if met, echo the $line value, but that feels very inefficient.

Any thoughts/input would be greatly appreciated.


Upvotes: 0

Views: 664

Answers (3)

Ed Morton
Ed Morton

Reputation: 204259

Assuming the answer you accepted does what you want, all you need is:

awk -F'[[:space:]/]+' '/In\/Out/ && ($NF - $(NF-1)) > 3' file

EDIT: given the sample input from your comment:

$ cat file
02:22:50 11/11/19 : Host1 Device1 In/Out 208/219

$ awk -F'[[:space:]/]+' '/In\/Out/ && ($NF - $(NF-1)) > 3' file
02:22:50 11/11/19 : Host1 Device1 In/Out 208/219

so if the script doesn't work for you then the most likely cause is you're using a pre-POSIX awk (e.g. nawk or a VERY old version of mawk) that doesn't support character classes. Given that, try hardcoding the blank and tab instead of [:space:]:

$ awk -F'[ \t/]+' '/In\/Out/ && ($NF - $(NF-1)) > 3' file
02:22:50 11/11/19 : Host1 Device1 In/Out 208/219

Upvotes: 0

tshiono
tshiono

Reputation: 22042

Would you try the following:

X=3                              # Or assign to whatever
pat="In/Out +([0-9]+)/([0-9]+)"  # Regex pattern to extract the times
while IFS= read -r line; do
    [[ $line =~ $pat ]] && (( ${BASH_REMATCH[2]} - ${BASH_REMATCH[1]} > X )) && echo "$line"
done < "logfile.log"

Test sample of logfile.log:

15:13:33   12/13/19 : Host1  Device1  In/Out 33/34
15:13:33   12/13/19 : Host1  Device1  In/Out 33/36
15:13:33   12/13/19 : Host1  Device1  In/Out 33/38
15:13:33   12/13/19 : Host1  Device1  In/Out 33/40

Output:

15:13:33   12/13/19 : Host1  Device1  In/Out 33/38
15:13:33   12/13/19 : Host1  Device1  In/Out 33/40

[EDIT]
According to the OP's info, the regex pattern has been updated:

X=3
pat="In/Out +\(([0-9]+)/([0-9]+)"
while IFS= read -r line; do
    [[ $line =~ $pat ]] && (( ${BASH_REMATCH[2]} - ${BASH_REMATCH[1]} > X )) && echo "$line"
done < "logfile.log"

Sample input:

15:13:33   12/13/19 : Host1  Device1  In/Out (33/34).
15:13:33   12/13/19 : Host1  Device1  In/Out (33/36).
15:13:33   12/13/19 : Host1  Device1  In/Out (33/38).
15:13:33   12/13/19 : Host1  Device1  In/Out (33/40).

Output:

15:13:33   12/13/19 : Host1  Device1  In/Out (33/38).
15:13:33   12/13/19 : Host1  Device1  In/Out (33/40).

The pattern In/Out +\(([0-9]+)/([0-9]+) is composed of:

  • In/Out ... The literal string
  • + ... One or more whitespace(s) (It may be illegible but there exists a whitespace before the plus sign.)
  • \( ... A literal left paren
  • ([0-9]+)/([0-9]+) ... One or more digit(s) folowed by a slash and one or more digits again. The surrounding parens generate capture groups and the matched substrings (digits, in this case) are stored in the bash variables ${BASH_REMATCH[1]} and ${BASH_REMATCH[2]} in order.

Hope this helps.

Upvotes: 1

KamilCuk
KamilCuk

Reputation: 141583

Such (kind-of-)complicated operations on files are usually done in a single awk script.

awk -v X=10000 '
function abs(v) {return v < 0 ? -v : v}
{ 
   if ($6 == "In/Out") {
      split($7, a, "/");
      if (abs(a[1] - a[2]) > X) {
           print
      }
    }
}' 

The script is almost human readable. First check if 6th field is In/Out. If it is, split 7th field on /, compute absolute value of the numbers and compare them with delta. If they compare "more than X", print the whole line.

Tested on repl. I think you will have to tweak the script to your needs.

Upvotes: 2

Related Questions