Compare a date in a bash script - bash

I am doing a script to evaluate the last connection of a user, for this, I get the last time it was connected, and I extract the user + the date of the last connection, what I need to do now is to see if that date is greater or less than "2022-05-20" for example, but my problem is, that I do not know how to compare two dates in bash.
This is my code;
while [ $i -le $size_students ]
do
# Get the user's login
login=$(sed -n "${i}p" xxxx.txt)
# Get the user's data
user_data=$(curl -X GET -H "Authorization: Bearer ${bearer_token}" "https://xxxxxxxxx/${login}/locations_stats" --globoff)
# Get the user's last location
last_lotaction=$(echo $user_data | jq -r '.' | cut -d "\"" -f 2 | grep -Eo '[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -n 1)
# if last_location is null or less than 2022-05-01, the user is not connected
echo `$login: $last_location`
The output is:
EnzoZidane: 2022-03-17

With your date formats, a simple string comparison should yield the desired result:
#!/bin/bash
[[ 2022-05-20 > 2022-05-19 ]] && echo yes
[[ 2022-05-20 < 2022-05-19 ]] || echo no

If you can guarantee that all your months and days are formatted with a leading zero where applicable, and the order is year-month-day, then you can probably just use a string comparison:
if [[ "2022-05-31" < "2022-06-01" ]]
then
echo true
fi

Convert all you dates to epoch first, then you can compare two integers:
if [[ $(date --date="$last_location" +%s) < $(date --date="2022-05-20" +%s) ]]; then
echo "before"
else
echo "after"
fi
Epoch time is always an integer number.

Related

Bash script to check if output of cmd increases over a period of time and if so run

I am looking to compare the output of a file in between time-frames.
Example:
Run command and it outputs a numerical value 1 to 1000 (1-1000). I need to run the same command 15 min later and see if it has increased by 20 and if so send email.
I am thinking this is very simple, however I cannot get my head around where to even start.
I would think that I would have to run the command and output to file and when the command runs again in 15 min compare those values. I would think that I would need to check if the output file exists and then create if not.
I am new to scripting and could use the help. I got the below but just am stuck beyond. I am looking to make this as simple as possible
#!/bin/bash
today="date '+%Y_%m_%d'"
command="echo "select count(status) from alarm where status ='open'" | my-db"
# the command above will return the following for example
#count(status)
#34
$command > /tmp/$today.dblogout
You could simply keep the last known (last retrieved) value in a temporary file, and every time the script is executed, check for how much the value has increased since last run (updating the last known value along the way).
For example:
#!/bin/bash
store=/tmp/last_value
threshold=20
last=$(<"$store")
current=$(my-db <<<"select count(status) from alarm where status = 'open'" | tee "$store")
if (( current - last > threshold )); then
echo "Sending mail..."
fi
I made something similar to alert me if my disk is getting full:
#!/bin/sh
THRESHOLD=85
datafile=/var/run/df_slash_var_pcent_avail.txt
# get previous value:
prev=$(tail -1 $datafile | cut -d\ -f2)
# get current value
curr=$(($(df /var --output=pcent | tail -1 | cut -d% -f1) + 0))
# store current value
echo "$(date +%s) $curr" >> $datafile
# if getting worse, alert
[[ $curr > $THRESHOLD ]] && [[ $curr > $prev ]] && smsme "/var grew from ${prev}% to ${curr}%"
This script is pretty straightforward. Feel free to ask if sth is not clear.
A couple of options
If alerts table has a date column you can write an SQL query to get the count for you.
Use bash to get the values
#!/bin/bash
# Store date in a variable, note the $( ) surrounding the command
today="$(date '+%Y_%m_%d')"
old=0
# read the old value from file if it exists
if [ -f /tmp/$today.dblogout ]; then
old="$(< /tmp/$today.dblogout)"
fi
# store just the value without any header
new=$(echo "select count(status) from alarm where status ='open'" | my-db)
# the command above will return the following for example
#34
# response is not empty
if [ -z "$new" ]; then
echo "old count: $old, new count: $new"
else
echo "new value is empty. old value: $old"
fi
# store the new value
echo "$new" > /tmp/$today.dblogout

Use different variables in one while loop or better use for loop? [duplicate]

This question already has answers here:
A variable modified inside a while loop is not remembered
(8 answers)
Closed 4 years ago.
please bear with me and my questions, but I just started programming in bash yesterday effectively.
I have a script that does a lot of stuff so far. And I came to a point where I need to iterate through variables. I created these variables before using a while loop. The $Time variable for example looks like this:
2016-01-29 17:07:00Z
2016-01-29 17:26:20Z
2016-01-29 17:26:20Z
2016-01-29 00:07:00Z
The Grabinterval variable like this:
hour
minute
minute
day
The first step for me is to check if a different variable is not empty. If it is not I go on with checking line per line inside the $Grabinterval variable what kind it is day, hour or minute.
That is done by a while loop as well and works good. But no the problems are rising. Now I want to calculate a time difference between a time that was created earlier outside of the while loop and the time given in the first line of the $Time variable. I tried this using the following code:
while read -r line; do
if [[ ! -z "$Filelocation" ]]
then
if [[ $line = "day" || $line = "days" ]]
then
Interval="Days"
GrabTimeNew=$(date +'%Y-%m-%d 00:0'$UPOFFSET':00Z')
default=$(date --date "-1 day" +'%Y-%m-%d 00:0'$UPOFFSET':00Z')
start=$(date -d"$line$Time" +%s)
end=$(date -d"$GRABtime" +%s)
TimeDiff=$(( $start - $end ))
fi
fi
done <<< $Grabinterval
This is only on part of the bigger while loop, the other two parts looking for hour and minuteare pretty much the same.
The way I tried it here using $line$Time gives me following error message:
date: invalid date ‘day2016-01-29 17:07:00Z\n2016-01-29 17:26:20Z\n2016-01-29 17:26:20Z\n2016-01-29 00:07:00Z’
So it goes over all lines instead of only the dayline I want it to go through. Is there a way to use the first line of the $Timevariable inside the $Grabinterval variable?
I would love to use a for loop, but I have no idea how to use it later in the command block to have the wanted line read in the command block of the if statement.
Thanks,
BallerNacken
EDIT: Tried something like this now, but not working either:
while read -r GI TI; do
if [[ ! -z "$Filelocation" ]]
then
if [[ $GI = "day" || $GI = "days" ]]
then
Interval="Days"
GrabTimeNew=$(date +'%Y-%m-%d 00:0'$UPOFFSET':00Z')
default=$(date --date "-1 day" +'%Y-%m-%d 00:0'$UPOFFSET':00Z')
start=$(date -d"$TI" +%s)
end=$(date -d"$GRABtime" +%s)
TimeDiff=$(( $start - $end ))
fi
if [[ $GI = "hours" || $GI = "hour" ]]
then
Interval="Hours"
GrabTimeNew=$(date +'%Y-%m-%d %H:0'$UPOFFSET':00Z')
default=$(date --date "-1 hour" +'%Y-%m-%d %H:0'$UPOFFSET':00Z')
start=$(date -d"$TI" +%s)
end=$(date -d"$GRABtime" +%s)
TimeDiff2=$(( $start - $end ))
fi
if [[ $GI = "min" || $GI = "minutes" || $GI = "minute" ]]
then
Interval="Minutes"
GrabTimeNew=$(date +'%Y-%m-%d %H:%M:20Z')
default=$(date --date "-1 minute" +'%Y-%m-%d %H:%M:00Z')
start=$(date -d"$TI" +%s)
end=$(date -d"$GRABtime" +%s)
TimeDiff3=$(( $start - $end ))
fi
fi
done < <(paste <(echo "$Grabinterval") <(echo "$Time"))
I don't get any error messages, but no variables were created inside the if statement/while loop.
You might want to try something like this:
while read -r interval datetime; do
#...
done < <(paste <(echo "$Grabinterval") <(echo "$Time"))
That will read one line from Grabinterval and the corresponding line from Time
You need to quote the variable to keep the newlines in the <<< $var.
list=$'a\nb\nc'
while read a ; do echo $a ; done <<< $list
while read a ; do echo $a ; done <<< "$list"

Stock date in a string then split it for tests

I have some operations to do on files last modified on a specific date. I would like to get the date, stock it in a string, then split it to test if the day corresponds to what I want.
So far, I've been trying things like that:
#!/bin/bash
for i in {45..236}; do
nom=M$i
chem=/Users/nfs/helene/soft/metAMOS-1.5rc3/$nom.fastq/Assemble/out
if [ -e $chem ]; then
IN= $(date -r $chem)
arr=(${IN//\ / })
if [[ ${arr[1]} == 'juin' && ${arr[2]} == '10' ]]; then
echo $nom
#cp $chem/proba.faa /Users/nfs/helene/metagenomes/DB/$nom.faa
fi
fi
done
exit 0
But it seems like the date isn't well stocked in $IN, and I'm not sure about the space-spliting either..
Perhaps the simple mistake is that you didn't place your assignment adjacent to =. It must be:
IN=$(date -r $chem)
And here's a simplified suggestion:
#!/bin/bash
for i in {45..236}; do
nom="M${i}"
chem="/Users/nfs/helene/soft/metAMOS-1.5rc3/${nom}.fastq/Assemble/out"
if [[ -e $chem ]]; then
read month day < <(exec date -r "$chem" '+%b %d')
if [[ $month == 'Jun' && $day == 10 ]]; then
echo "$nom"
# cp "$chem/proba.faa" "/Users/nfs/helene/metagenomes/DB/$nom.faa"
fi
fi
done
exit 0
* See date --help for a list of formats.
* <() is a form of Process Substitution. Check Bash's manual for it.
* Always place your arguments around double quotes when they have variables to avoid word splitting.

Bash script compare two date variables [duplicate]

This question already has answers here:
How can I calculate time elapsed in a Bash script?
(20 answers)
Closed 5 years ago.
I'm trying to compare a date given by a user to a date in a file, basically a text file with lots of dates and times listed.
for example the user would enter a date such as 22/08/2007 and a time of 1:00, what i need the script to do is count how many dates in the text file are after the date given by the user.
I’ve managed to accomplish this by converting each date in the text file to unix timestamp and then comparing the two. Is there no way of simply comparing two dates in bash?
Thanks in advance
The GNU date command can convert a date into the number of seconds since 1970. Try this script:
#! /bin/bash
DATE=$(date -d "$3-$2-$1 01" '+%s')
COUNT=0
tr '/' ' ' | {
while read D M Y ; do
THIS=$(date -d "$Y-$M-$D 01" '+%s')
if (( THIS > DATE )) ; then
COUNT=$((COUNT + 1))
fi
done
echo $COUNT
}
It expects three arguments and the raw dates in stdin:
for D in $(seq 19 25) ; do echo $D/08/2007 ; done | ./count.sh 22 08 2007
3
It will work till 2038. ;-)
If you don't mind an external helper tool look at my dateutils. Your use case is covered by
dgrep -i '%d/%m/%Y %H:%M' '>=2007-08-22 01:00:00' < FILE | wc -l
where FILE is your file with the dates, and -i specifies the date format used in the file (I assumed dates like 22/08/2007 01:00 here). Matching lines will be printed, hence counting them gives you the information you were after.
...why don't you simply cut out the single numbers, rearrange them from the most signifcant to the less significant, put them toghether to form a new big number and then compare the other one? :) Suppose you have a date in both $1 and $2 and suppose the date format is dd-mm-yyyy (adding hours and minutes is trivial):
d1=`echo "$1" | cut -d "-" -f 1`
m1=`echo "$1" | cut -d "-" -f 2`
y1=`echo "$1" | cut -d "-" -f 3`
date1="$y1$m1$d1"
d2=`echo "$2" | cut -d "-" -f 1`
m2=`echo "$2" | cut -d "-" -f 2`
y2=`echo "$2" | cut -d "-" -f 3`
date2="$y2$m2$d2"
if [ "$date1" -gt "$date2" ]; then
#date1 > date2
else
#date2 >= date1
fi
Note that you need zeros for 1-digit fields, for example, dates like this will work:
01-01-2013
and dates like this will NOT
1-1-2013
Cheers :-)
The problem is that dates are printed in such a way that, string-wise, "1/1/2050 1:00" < "2/1/1999 0:00". And since there's no way for a script to know that something is a datetime without you saying so, you essentially have to convert any date to something that can be compared - Either you have to order the elements so that the most important (year) are first, etc. (like ISO dates) or you convert to a number.
the above command compares the date in form of integer and would work fine until you are comparing the dates of same year.
better idea is to break the dates into 3 parts of dd, mm and yyyy and then do a comparison. just as below:
sysdate=`date +%d%m%Y`
sys_dd=`echo $sysdate|cut -c1,2`
sys_mm=`echo $sysdate|cut -c3,4`
sys_yyyy=`echo $sysdate|cut -c5-8`
cd $dir_source #moving in directory where report are placed
for i in *.* #reading all the files present in directory and comparing with current sysdate
do
filename=$i
filedate=`echo $filename| cut -d '_' -f1`
file_dd=`echo $filedate|cut -c1,2`
file_mm=`echo $filedate|cut -c3,4`
file_yyyy=`echo $filedate|cut -c5-8`
if [ $sys_yyyy -lt $file_yyyy ]
then
echo "future cob file, check for the error"elif [ $sys_yyyy -gt $file_yyyy ]
then
echo "prev cob file , to be removed"
else
if [ $sys_mm -lt $file_mm ]
then
echo "future cob file, check for the error"
elif [ $sys_mm -gt $file_mm ]
then
echo "prev cob file , to be removed"
else
if [ $sys_dd -lt $file_dd ]
then
echo "future cob file, check for the error"
elif [ $sys_dd -gt $file_dd ]
then
echo "prev cob file , to be removed"
else
echo "file date is same is cob date, retaining the file as it is"
fi
fi
fi

bash loop between two given dates

I'm trying to create a script that will loop through files that have their filenames written in the following format: yyyymmdd.hh.filename.
The script is called with:
./loopscript.sh 20091026.00 23
./loopscript.sh 20091026.11 15
./loopscript.sh 20091026.09 20091027.17
The need is for the script to check each hour between those two given dates/hours.
e.g.
cat 20091026.00.filename |more
cat 20091026.01.filename |more
...
cat 20091026.23.filename |more
cat 20091027.01.filename |more
cat 20091027.02.filename |more
...
and so on.
any idea how to go about this? I don't have any difficulty with standard 0 - x loops. or simple for loops. Just not sure how to go about the above.
How about this:
#!/bin/bash
date1=$1
date2=$2
#verify dates
if ! date -d "$date1" 2>&1 > /dev/null ;
then echo "first date is invalid" ; exit 1
fi
if ! date -d "$date2" 2>&1 > /dev/null ;
then echo "second date is invalid" ; exit 1
fi
#set current and end date
current=$(date -d "$date1")
end=$(date -d "$date2 +1 hours")
#loop over all dates
while [ "$end" != "$current" ]
do
file=$(date -d "$current" +%Y%m%d.%H)
cat $file."filename" | more
current=$(date -d "$current +1 hours")
done
To process each file between two given date/hours, you can use the following:
#!/usr/bin/bash
#set -x
usage() {
echo 'Usage: loopscript.sh <from> <to>'
echo ' <from> MUST be yyyymmdd.hh or empty, meaning 00000000.00'
echo ' <to> can be shorter and is affected by <from>'
echo ' e.g., 20091026.00 27.01 becomes'
echo ' 20091026.00 20091027.01'
echo ' If empty, it is set to 99999999.99'
echo 'Arguments were:'
echo " '${from}'"
echo " '${to}'"
}
# Check parameters.
from="00000000.00"
to="99999999.99"
if [[ ! -z "$1" ]] ; then
from=$1
fi
if [[ ! -z "$2" ]] ; then
to=$2
fi
## Insert this to default to rest-of-day when first argument
## but no second argument. Basically just sets second
## argument to 23 so it will be transformed to end-of-day.
#if [[ ! -z "$1"]] ; then
# if [[ -z "$2"]] ; then
# to=23
# fi
#fi
if [[ ${#from} -ne 11 || ${#to} -gt 11 ]] ; then
usage
exit 1
fi
# Sneaky code to modify a short "to" based on the start of "from".
# ${#from} is the length of ${from}.
# $((${#from}-${#to})) is the length difference between ${from} and ${to}
# ${from:0:$((${#from}-${#to}))} is the start of ${from} long enough
# to make ${to} the same length.
# ${from:0:$((${#from}-${#to}))}${to} is that with ${to} appended.
# Voila! Easy, no?
if [[ ${#to} -lt ${#from} ]] ; then
to=${from:0:$((${#from}-${#to}))}${to}
fi
# Process all files, checking that they're inside the range.
echo "From ${from} to ${to}"
for file in [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].[0-9][0-9].* ; do
if [[ ! ( ${file:0:11} < ${from} || ${file:0:11} > ${to} ) ]] ; then
echo " ${file}"
fi
done
When you create the files 20091026.00.${RANDOM} through 20091028.23.${RANDOM} inclusive, this is a couple of sample runs:
pax> ./loopscript.sh 20091026.07 9
From 20091026.07 to 20091026.09
20091026.07.21772
20091026.08.31390
20091026.09.9214
pax> ./loopscript.sh 20091027.21 28.02
From 20091027.21 to 20091028.02
20091027.21.22582
20091027.22.30063
20091027.23.29437
20091028.00.14744
20091028.01.6827
20091028.02.10366
pax> ./loopscript.sh 00000000.00 99999999.99 # or just leave off the parameters.
20091026.00.25772
20091026.01.25964
20091026.02.21132
20091026.03.3116
20091026.04.6271
20091026.05.14870
20091026.06.28826
: : :
20091028.17.20089
20091028.18.13816
20091028.19.7650
20091028.20.20927
20091028.21.13248
20091028.22.9125
20091028.23.7870
As you can see, the first argument must be of the correct format yyyymmdd.hh. The second argument can be shorter since it inherits the start of the first argument to make it the correct length.
This only attempts to process files that exist (from ls) and of the correct format, not every date/hour within the range. This will be more efficient if you have sparse files (including at the start and the end of the range) since it doesn't need to check that the files exist.
By the way, this is the command that created the test files, if you're interested:
pax> for dt in 20091026 20091027 20091028 ; do
for tm in 00 01 02 ... you get the idea ... 21 22 23 ; do
touch $dt.$tm.$RANDOM
done
done
Please don't type that in verbatim and then complain that it created files like:
20091026.you.12345
20091028.idea.77
I only trimmed down the line so it fits in the code width. :-)
One possible solution: convert dates into standard Unix representation of "Seconds passed since the epoch" and loop, increasing this number by 3600 (number of seconds in an hour) each iteration. Example:
#!/bin/bash
# Parse your input to date and hour first, so you get:
date_from=20090911
hour_from=10
date_to=20091026
hour_to=01
i=`date --date="$date_from $hour_from:00:00" +%s`
j=`date --date="$date_to $hour_to:00:00" +%s`
while [[ $i < $j ]]; do
date -d "1970-01-01 $i sec" "+%Y%m%d.%H"
i=$[ $i + 3600 ]
done

Resources