How can I retrieve logs between two timeframes using regex? - regex

I have a huge log file, where each line is a log entry with it's own timestamp. How can I retrieve logs in-between two specified timestaps (ie. start time - 22:00:00, end time - 23:00:00)?

Using bash, it is possible to generate a regex statement based only on two input timestamps, which you may pipe to a command (such as grep):
#!/bin/bash
#This is a bare-bone recursive script that accepts input of a start/end timeframe
#in the 24-hour format, based on which a regex statement will be generated that will
#select all values in between those two timeframes. Please note that this script
#has undergone some optimization (I apologize for the reading difficulty, but it
#did not occur to me until now that someone might have a use for such a script).
#You are free to use, distribute, or modify this script to your heart's content. Any
#constructive feedback is welcome. I did my best to eliminate all bugs, but should
#you find any case where the generated regex is INCORRECT for some timestamps, please
#let me know.
echo $0 - Args passed: $1 $2
START_TIME=$(echo $1 | tr -d ":")
END_TIME=$(echo $2 | tr -d ":")
DIFF_CHAR=""
REGEX=""
function loop ()
{
diffcharValue=$1
maxVal=$2
while [ $diffcharValue -lt $maxVal ]
do
REGEX="${REGEX}:[0-5][0-9]"
let diffcharValue+=2
done
}
function regexGen ()
{
diffChar=$1
start=$2
end=$3
if [ $diffChar -le 6 ]; then
regexGen $(($diffChar + 1)) $start $end 0 #the fourth arg acts as a recursion indicaton, whether the function was called recursively or not
let diffChar-=1
diffCharMinusOne=$(($diffChar - 1))
startBegin=${start:0:$diffCharMinusOne}
endBegin=${end:0:$diffCharMinusOne}
startCut=${start:$diffCharMinusOne:1}
endCut=${end:$diffCharMinusOne:1}
endStartDiff=$(($endCut-$startCut))
if [ $(($diffChar % 2)) -eq 0 ]; then
if [ $4 -eq 0 ]; then
REGEX="${REGEX}$startBegin[$startCut-9]"
loop $diffChar 6
REGEX="${REGEX}|$endBegin[0-$endCut]"
loop $diffChar 6
REGEX="${REGEX}|"
elif [ $endStartDiff -gt 1 ]; then
if [ $endStartDiff -eq 2 ]; then
REGEX="${REGEX}$startBegin[$(($startCut+1))]"
else
REGEX="${REGEX}$startBegin[$(($startCut+1))-$(($endCut-1))]"
fi
loop $diffChar 6
echo $REGEX
else
echo ${REGEX%?}
fi
else
if [ $4 -eq 0 ]; then
if [ $startCut -lt 5 ]; then
REGEX="${REGEX}$startBegin[$startCut-5][0-9]"
loop $diffChar 5
REGEX="${REGEX}|"
fi
if [ $endCut -gt 0 ]; then
REGEX="${REGEX}$endBegin[0-$endCut][0-9]"
loop $diffChar 5
REGEX="${REGEX}|"
fi
elif [ $endStartDiff -gt 1 ]; then
if [ $diffCharMinusOne -gt 0 ]; then
REGEX="${REGEX}$startBegin"
fi
if [ $endStartDiff -eq 2 ]; then
REGEX="${REGEX}[$(($startCut+1))][0-9]"
else
REGEX="${REGEX}[$(($startCut+1))-$(($endCut-1))][0-9]"
fi
loop $diffChar 5
echo $REGEX
else
echo ${REGEX%?}
fi
fi
fi
}
for a in {0..5}
do
if [ ${END_TIME:$a:1} -gt ${START_TIME:$a:1} ];then
DIFF_CHAR=$(($a+1))
break
fi
done
result=$(regexGen $DIFF_CHAR $START_TIME $END_TIME 1 | sed 's/\([0-9][0-9]\)/\1:/g')
echo $result

Related

How do I remove multiple bracket within nested bracket using sed?

I want to make a program editing file code with shell programming
there is command 'Remove ${} in arithmetic expansion$(())'
and I have a problem in implementation.
I'm going to make bash shell code below
cnt=$(( ${cnt} + ${cnt123} ))
to
cnt=$(( cnt + cnt123 ))
I want to remove command substitution bracket in arithmetic Expansion bracket
I tried to do with this regex expression:
sed -Ei 's/(\$\(\()([^\)]*?)\$\{([^\}]+?)\}(.*?)(\)\))/\1\2\3\4\5/g' $file
but, it just found a longest one.(even though there are another match before matched one)
if you want to see visualized regex expression, click this link visualized image
result showed like this:
cnt=$(( ${cnt} + cnt123 ))
How to do remove internal bracket in nested bracket?
(I should just use awk or sed, but if it could be impossible, it doesn't matter using other bash command)
works example:
s=$(( ${s} ** 2 ))
to
s=$(( s ** 2 ))
sum=$(( ${a} + ${b} ))
to
sum=$(( a + b ))
echo $(( (${var} * ${var2}) / ${var3} ))
to
echo $(( (var * var2) / var3 ))
echo ${d} $((${t1} + ${t2})) ${e}
to
echo ${d} $(( t1 + t2 )) ${e}
:my sample input file (it doesn't matter what it do for)
#! /bin/bash
cnt=0
cnt123=1
for filename in *
do
fname=$(basename $filename)
cname=$(echo $fname | tr A-Z a-z)
if [ "$fname" != "$cname" ]
then
if [ -e "$cname" ]
then
echo "$cname already exists"
exit 1
fi
echo "$fname is renamed $cname"
mv $fname $cname
cnt=$(( ${cnt}+ ${cnt123} ))
fi
done
echo "Total count: $cnt"
exit 0
As it is not an easy task for sed/awk regex and relevant functions,
please let me show a perl solution, although perl is not tagged in
your question.
perl -i -pe 's/(?<=\$\(\().+?(?=\)\))/ $_ = $&; s#\$\{(.+?)}#$1#g; $_ /ge' "$file"
Input file example:
s=$(( ${s} ** 2 ))
sum=$(( ${a} + ${b} ))
echo $(( (${var} * ${var2}) / ${var3} ))
echo ${d} $((${t1} + ${t2})) ${e}
Modified result:
s=$(( s ** 2 ))
sum=$(( a + b ))
echo $(( (var * var2) / var3 ))
echo ${d} $((t1 + t2)) ${e}
The perl substitution s/pattern/expression/e replaces the matched
pattern with the perl expression instead of literal (or fixed)
replacement. You can perform a dynamic replacement with this mechanism.
(?<=\$\(\().+?(?=\)) matches a substring which is preceded by $((
and followed )). Then the matched substring will be the content within
$(( .. )). The perl variable $& is assigned to it.
The expression $_ = $&; s#\$\{(.+?)}#$1#g; $_ is a perl code to remove
the paired ${ and } from the matched substring above. The g option
after the delimiter # works as that of sed.
Depending on the context you may want to limit the extent of the match to only alphanumeric chars
sed -Ei.bak 's/\$\{([[:alnum:]]+)\}/\1/g'
to avoid unintentionally matching something else.
Using sed
$ sed -Ei.bak 's/\$\{([^}]*)}/\1/g' input_file
cnt=$(( cnt + cnt123 ))
printf="cnt=\$(( \${cnt} + \${cnt123} ))" | tr -d '{' | tr -d '}'
I used printf here because it is slightly more trustworthy than echo. Make sure you escape $, otherwise you will get an error because of bash trying to interpret the variables.

Trouble with Errors on Unix sh with if statements

I am having a problem with the code below with if statements. It has three different arguments, I'm not sure if one or all of them are wrong.
#!/bin/sh
1=C
2=S
(1/2)=C <---- This might be a problem
if [ $1 == "C*" ]
then
echo "The city is:"
else [ $2 == "S*" ]
then
echo "The state is:"
else [ $1 -a $2 == "B*" ]
then
echo "The city and state is:"
else [ $1 -o $2 -ne "C*" "S*" "B*" ]
then
echo "Not found!"
fi
echo End of script
Using numbers as variables is very unwise idea. Numbers are used to represent command line parameters.
Also this construction is AFAIK unknown to bash: (1/2)=C
Also this construction:
else [ $2 == "S*" ]
then
is wrong, should be some something like:
elif [ $2 == "S*" ]
then
Also this:
else [ $1 -o $2 -ne "C*" "S*" "B*" ]
is odd, you use -ne which is for compare numbers, for strings use !=
And also a lot of other errors/problems challenging constructions I have no time to mention.

unexpected result using regex in bash

I made a trying to make regex expression that will validate a number that is in the range of -100 to 100.
the regex expression I made is ^[-+]?([0-9][0-9]?|100)$.
I am looking for a pattern in a string not just an integer by itself.
this is my script:
#!/bin/bash
a="input2.txt"
while read -r line; do
mapfile -t d <<< "$line"
for i in "${d[#]}"; do
if [[ "$i" =~ ^[-+]?([0-9][0-9]?|100)$ ]]; then
echo "$i"
fi
done
done < "$a"
this is my input file:
add $s1 $s2 $s3
sub $t0
sub $t1 $t0
addi $t1 $t0 75
lw $s1 -23($s2)
the actual result is nothing.
the expected result:
75 -23($s2)
[...] denotes a set of characters, where the dash can be used to specify a character range. For instance, [4-6u-z] in a regexp means one of the characters 4,5,6,u,v,w,x,z. Your expression [1-200] simply matches the characters (digits) 0, 1 and 2.
In your case, I would therefore proceed in two steps: First, extract from your string the initial numeric parts, and then use arithmetic comparision on the result. For example (not tested!):
if [[ $i =~ ^-?[0-9]+ ]]
then
intval=${BASH_REMATCH[0]}
if (( intval >= -200 && intval <= 1000 ))
then
....
See the bash man page for an explanation of the BASH_REMATCH array.
#first store your file in an array so that we could pass thru the words
word_array=( $(<filename) )
for i in "${word_array[#]}"
do
if [[ $i =~ ^([[:blank:]]{0,1}-?[0-9]+)([^[:digit:]]?[^[:blank:]]*)$ ]]
#above line looks for the pattern while separating the number and an optional string
#that may follow like ($s2) using '()' so that we could access each part using BASH_REMATCH later.
then
#now we have only the number which could be checked to fall within a range
[ ${BASH_REMATCH[1]} -ge -100 ] && [ ${BASH_REMATCH[1]} -le 100 ] && echo "$i"
fi
done
Sample Output
75
-23($s2)
Note : The pattern might need a bit more testing, but you could imbibe the idea.

Perl regex replacement in file skips newlines

I am currently trying to format text for use with pandoc, but my regex replacement isn't working.
Here is my code so far:
#Importing the file
my $filename = 'example.md';
my $file = path($filename);
my $data = $file ->slurp_utf8;
#Placing code blocks into an array and replacing them in the file
my #code_block_values;
my $i = 0;
while($i > 50) {
#code_bock_values[$i] = ($data =~ /\n\t*```[^`]+(```){1}\n/);
$data =~ s/\n\t*```[^`]+(```){1}\n/(code_block)/;
$i = $i + 1;
}
#Replacing the code blocks
$i = 0;
while($i < 50) {
$data =~ s/\(code_block\)/$code_block_values[$i]/;
$i = $i + 1;
}
print $data;
$file->spew_utf8( $data );
I realize that this probably isn't the most efficient way to be doing this, but right now I'm just trying to get it working.
Basically, I am using github-flavored markdown for typing up my notes, and then trying to convert it with pandoc to a pdf file. I am doing some other formatting before-hand, but I have to extract the code blocks first (which are deliniated by triple backticks (```).)
The following is a sample code block that would be a code block:
```bash
#!/bin/bash
echo "Enter a number"
read count
if [ $count -eq 100 ]
then
echo "Example-3: Count is 100"
elif [ $count -gt 100 ]
then
echo "Example-3: Count is greater than 100"
else
echo "Example-3: Count is less than 100"
fi
```
As far as I can tell, the regex is capturing everything that I need (as tested by an online regex tester), but Perl is only inserting newlines at certain points, specifically newlines followed by a tab.
The previous example translates to:
```bash #!/bin/bash echo "Enter a number" read count if [ $count -eq 100 ] then
echo "Example-3: Count is 100" elif [ $count -gt 100 ] then
echo "Example-3: Count is greater than 100" else
echo "Example-3: Count is less than 100" fi
```
As you can see, the tabs are also completely removed. I copied over all of the file contents from atom and the different lengths of tabs are as copied over from the editor (not sure that makes a difference.) I did my editing for the shell script in vim but the editing for the notes in atom itself.
I am new to Perl, so any help would be appreciated.
My guess:
#!/usr/bin/perl
use strict;
use warnings;
# Slurps DATA after __END__ into a scalar
my $data = do { local $/; <DATA> };
my #code_block_values;
# Extract and replace code blocks with '(code_block)'
while ($data =~ s/ (``` .*? ```) /(code_block)/xs) {
push #code_block_values, $1;
}
printf "\n--| Replaced:\n%s", $data;
# Restore '(code_block)' with actual content
$data =~ s/ \(code_block\) / shift #code_block_values /xge;
printf "\n--| Restored:\n%s", $data;
__END__
```bash
#!/bin/bash
echo "Enter a number"
read count
if [ $count -eq 100 ]
then
echo "Example-3: Count is 100"
elif [ $count -gt 100 ]
then
echo "Example-3: Count is greater than 100"
else
echo "Example-3: Count is less than 100"
fi
```
```perl
#!/usr/bin/perl
print "Hello World\n";
```
Output:
--| Replaced:
(code_block)
(code_block)
--| Restored:
```bash
#!/bin/bash
echo "Enter a number"
read count
if [ $count -eq 100 ]
then
echo "Example-3: Count is 100"
elif [ $count -gt 100 ]
then
echo "Example-3: Count is greater than 100"
else
echo "Example-3: Count is less than 100"
fi
```
```perl
#!/usr/bin/perl
print "Hello World\n";
```
If you want to learn more about Perl's regular expressions, perlre and perlretut is a good read.

function to validate dates using regex in bash

I have function like following to be able to validate two dates passed to it:
function validate_dates() {
# validate date format to be yyyy-mm-dd
local regex="^[[:digit]]{4}-[[:digit]]{2}-[[:digit]]{2}$"
local dates=( "$1" "$2" )
printf "%s\n" "${dates[#]}"
for __date in "$dates"
do
echo "$__date"
[[ $__date =~ $regex ]] || error_exit "One of dates is malformed!" # error_exit is just function helper to exit with message
done
}
However when I call function -
validate_dates "2013-05-23" "2014-07-28"
I get:
2013-05-23
2014-07-28
2013-05-23
One of dates is malformed!
Why does it break on correctly formatted date?
"^[[:digit]]{4}-[[:digit]]{2}-[[:digit]]{2}$"
probably should be:
"^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}$"
You missed some colons.
Also, for __date in "$dates" perhaps should be for __date in "${dates[#]}". "$dates" would only expand to the first element of the array.
If you want to have more than just 2 parameters or have variable parameters, change
local dates=( "$1" "$2" )
to
local dates=("$#")
My version:
function validate_dates {
local regex='^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}$'
printf "%s\n" "$#"
for __; do
[[ $__ =~ $regex ]] || error_exit "$__ is an invalid date."
done
}
Please note you cannot "validate date" using only regex. A second pass using date might be useful:
for __date in "$dates"
do
echo "$__date"
{ [[ $__date =~ $regex ]] && (date -d "$__date" > /dev/null 2>&1) } ||
error_exit "One of dates is malformed!" # error_exit is just function helper to exit with message
done
Using grep with -P makes the regex much easier to read, and you can simplify your function as below:
function validate_dates {
for i in $#; do
grep -qP '^\d{4}-\d{2}-\d{2}$' <<< $i ||\
error_exit "One of dates is malformed!"
done
}