To start, I have done a lot of googling and tried grep, sed and awk, but was not able to find a solution with either.
My goal is to find 2 patterns in a log file, but only if each first pattern has a matching second pattern (with no matching first patterns between them). After that I will compare the timestamps on both to calculate the time between, but that part is not where I am stuck.
Almost every solution I found via google left me with all of the lines between the start and end (which I do not need) or they left me with the first start match and the first end match (with multiple starts in between)
Example of the kind of text I am working with:
2022-09-10 20:17:05.552 [INFO] Starting process
2022-09-10 20:17:05.554 [INFO] junk here
2022-09-10 20:24:02.664 [INFO] junk here
2022-09-10 20:24:02.666 [INFO] Starting process
2022-09-10 20:30:57.526 [INFO] Starting process
2022-09-10 20:30:57.529 [INFO] Ending process
2022-09-10 20:37:55.122 [INFO] Starting process
2022-09-10 20:37:55.126 [INFO] Ending process
2022-09-10 20:44:50.352 [INFO] junk here
I want to find the lines with "Starting process" and then "Ending process" but with no "Starting process" between them (multiple starts without an end are failed attempts and I only need the ones that completed). The example has multiple failed starts, but only 2 starts that completed: Lines 5-6 and 7-8
Expected output:
2022-09-10 20:30:57.526 [INFO] Starting process
2022-09-10 20:30:57.529 [INFO] Ending process
2022-09-10 20:37:55.122 [INFO] Starting process
2022-09-10 20:37:55.126 [INFO] Ending process
Actually, the only output I really need would be:
2022-09-10 20:30:57.526
2022-09-10 20:30:57.529
2022-09-10 20:37:55.122
2022-09-10 20:37:55.126
(because my only need for these lines is to get the start and end time to calculate average time for this task when it completes)
I am willing to use most command line methods available via bash on Ubuntu (this is for a windows machine with WSL), so sed/awk/grep and possibly even perl are fine.
CodePudding user response:
Here's a solution with awk
for getting the matching dates:
awk -F ' \\[[^[]*] ' '
$2 == "Starting process" { d = $1 }
$2 == "Ending process" && d != "" { print d, $1 ; d = "" }
'
2022-09-10 20:30:57.526 2022-09-10 20:30:57.529
2022-09-10 20:37:55.122 2022-09-10 20:37:55.126
If you're using GNU awk then you can even calculate the time difference:
awk -F ' \\[[^[]*] ' '
function date2time(d, _d) {
_d = d
gsub( /[T:-]/, " ", _d )
return mktime(_d) substr(d, index(d,"."))
}
$2 == "Starting process" {
t = date2time($1)
}
$2 == "Ending process" && t != "" {
printf "%.03f\n", date2time($1) - t
t = ""
}
'
0.003
0.004
CodePudding user response:
you can try one routine like that:
#!/bin/bash
######################################################################
# Finds the matching pair of two patterns
# Arguments:
# l_start_pattern - The first pattern
# l_end_pattern - The second pattern
# l_filename - Filename that we will run through each line of it
######################################################################
function find_matching_pair() {
local l_start_pattern="$1"
local l_end_pattern="$2"
local l_filename="$3"
local l_record
local l_start_pattern_found=false
local l_start_record=""
if [[ -z "${l_filename}" ]]; then
return
fi
while read l_record ; do
if [[ ${l_record} =~ ^.*${l_start_pattern} ]]; then
l_start_pattern_found=true
l_start_record="${l_record}"
fi
if [[ ${l_record} =~ ^.*${l_end_pattern} ]] &&
[[ ${l_start_pattern_found} == true ]]; then
echo "${l_start_record}"
echo "${l_record}"
l_start_pattern_found=false
l_start_record=""
fi
done < ${l_filename}
}
#
# Calls the routine and cut the output at the '[' delimiter,
# since you just need the timestamp
#
find_matching_pair "Starting process" "Ending process" "file.txt" | \
cut -d'[' -f 1
CodePudding user response:
This might work for you (GNU sed):
sed -nE '/ \[.*Starting.*/,/ \[.*Ending.*/{s///p}' file
Match a range, between Starting
and Ending
and print only the first and last lines amended.
Or alternatively:
sed -nE '/Starting/{:a;N;/Ending/!ba;s/ \[.*\n(.*) \[.*/\n\1/p}' file
CodePudding user response:
Using GNU sed
$ sed -En '/Starting process/t;{N;/Ending process/s/\[[^\n]*//gp}' input_file
2022-09-10 20:30:57.526
2022-09-10 20:30:57.529
2022-09-10 20:37:55.122
2022-09-10 20:37:55.126