Can someone tell me what I'm doing wrong, please?
I've got this block of code
if [ -n "${MFA_Exp}" ]; then
exp_sec="$(expr '(' $(date -d "${MFA_Exp}" +%s) - $(date +%s) ')' )";
if [ "${exp_sec}" -gt 0 ]; then
output+=", MFA TTL: $(date -u -d #"${exp_sec}" +"%Hh %Mm %Ss")";
else
output+=", MFA DEAD!";
fi;
that should output the expiration time of my MFA token, but I get this error
date: option requires an argument -- d
usage: date [-jnRu] [-d dst] [-r seconds] [-t west] [-v[+|-]val[ymwdHMS]] ...
[-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]
I'm on a Macbook and I suspect it's something to do with the date format.
I'm just not sure what it is.
The default date format for BSD date is [[[mm]dd]HH]MM[[cc]yy][.ss]]. If MFS_Exp is in that format, you can use
exp_sec=$(( $(date -j "$MFS_Exp" +%s) - $(date +%s) ))
If not, you need to specify the input format using the -f option. For example, if your string is like 2020-12-18 12:34:56, then use date -j -f '%Y-%m-%d %H:%M:%S' "$MFS_Exp" +%s.
For the second call, I wouldn't recommend using date at all, as you are working with a duration, not a timestamp.
hours=$(( exp_sec / 3600 ))
rem=$(( exp_sec % 3600 ))
minutes=$(( rem / 60 ))
sec=$(( rem % 60 ))
output+=", MFA TTL: ${hours}h ${minutes}m ${sec}s"
Related
I'm trying to set the difference (in hours, minutes and seconds) between two dates into a variable. The format is +%y%m%d%H%M%S (for example: 170607162412).
For example: 170607162400 and 170607162410 = 00:00:10
I tried a lot but i couldn't figure it out.
Take a look here: http://www.unix.com/tips-and-tutorials/31944-simple-date-time-calulation-bash.html.
The trick is to convert your date to a timestamp (seconds since Jan 01 1970. UTC). Than you can add and remove seconds and even substract dates from each other.
date2stamp () {
date --utc --date "$1" +%s
}
stamp2date (){
date --utc --date "1970-01-01 $1 sec" "+%Y-%m-%d %T"
}
dateDiff (){
case $1 in
-s) sec=1; shift;;
-m) sec=60; shift;;
-h) sec=3600; shift;;
-d) sec=86400; shift;;
*) sec=86400;;
esac
dte1=$(date2stamp $1)
dte2=$(date2stamp $2)
diffSec=$((dte2-dte1))
if ((diffSec < 0)); then abs=-1; else abs=1; fi
echo $((diffSec/sec*abs))
}
Use date to put into seconds, then subtract. Then parse out minutes and seconds:
$ var1=170607162400
$ var2=170607162410
$ var="$var1"
$ date1="20${var:0:2}/${var:2:2}/${var:4:2 {var:6:2}:${var:8:2}:${var:10:2}"
$ var="$var2"
$ date2="20${var:0:2}/${var:2:2}/${var:4:2} ${var:6:2}:${var:8:2}:${var:10:2}"
$ sec1=$( date -d "$date1" '+%s' )
$ echo $sec1
1496867040
$ sec2=$( date -d "$date2" '+%s' )
$ echo $sec2
1496867050
$ dt=$(( sec2 - sec1 ))
$ echo $dt
10
$ min=$(( dt/60 ))
$ sec=$(( dt - 60*min ))
$ minsec=$( printf "%02d:%02d" "$min" "$sec" )
$ echo "$minsec"
00:10
If you need hours, too, change those last lines like so:
$ hrs=$(( dt/3600 ))
$ min=$(( (dt - 3600*hrs) / 60 ))
$ sec=$(( dt - 3600*hrs - 60*min ))
$ hms=$( printf "%d:%02d:%02d" "$hrs" "$min" "$sec" )
Using BASH I want to loop from a start to end date at ten-minute intervals.
I tried
begin_date="2015-01-01 00:00:00"
end_date="2015-02-20 00:00:00"
d=$begin_date
while [ "$d" != "$end_date" ]; do
echo $d
d=$(date -d "${d} + 10 min" +"%Y-%m-%d %H:%M")
done
But it didn't work. Looking at Bash:Looping thru dates
#This works
d=$(date -I -d "${d} + 1 day")
#This doesn't work
d=$(date -d "${d} + 1 day" +"%Y-%m-%d")
What am I missing in the format string?
The expression date -d "${d} + 10 min" seems not to produce a date with an offset of 10 minutes. In fact, when I run your code, I see a date counter going backwards. (Posting this diagnostic as part of your question would help others see where the problem is; you should not require others to run your code just to see what it does.)
Anyway, the sane way to do this is to convert the dates to Unix epoch, then take it from there.
for ((d=$(date -d "$begin_date" +%s); d <= $(date -d "$end_date" +%s); d += 600))
do
date -d #$d +"%F %H:%M"
done
Doing date arithmetic in the shell is probably going to be rather inefficient; converting this to e.g. Awk or Perl might be worth your time if you find it's too sluggish, or need to run it lots of times.
The example you linked to just needs to be adjusted slightly:
#!/bin/bash
## User-specified dates.
# See [GNU Coreutils: Date] for more info
# [GNU Coreutils: Date]: https://www.gnu.org/software/coreutils/manual/html_node/Combined-date-and-time-of-day-items.html#Combined-date-and-time-of-day-items
begin_date="2015-01-01T00:00"
end_date="2015-01-01T00:40"
# Run through `date` to ensure iso-8601 consistency
startdate=$(date --iso-8601='minutes' --date="${begin_date}")
enddate=$(date --iso-8601='minutes' --date="${end_date}")
# Do loop
d="$startdate"
while [ "$d" != "$enddate" ]; do
echo $d
d=$(date --iso-8601='minutes' --date="$d + 10 minutes")
done
Note that the options -I and -d are equivalent to --iso-8601 and --date respectively.
I trying to print date between 2 dates using while loop in a bash script.
But when i execute i am getting below error:
test.sh: line 8: [: 02-12-14: integer expression expected
Below is my code, can anyone help me out
#!/bin/bash
sdate=02-12-14
edate=02-25-14
while [ "$sdate" -le "$edate" ]
do
echo $sdate
sdate=$(date +%m-%d-%y -d "$sdate + 1 day")
done
You should store them as timestamps:
#!/bin/bash
sdate=$(date -d '2014-02-12' +%s)
edate=$(date -d '2014-02-25' +%s)
while [[ sdate -le edate ]]; do
date -d "#$sdate" '+%m-%d-%y'
sdate=$(date -d "$(date -d "#${sdate}" ) + 1 day" +%s)
done
Output:
02-12-14
02-13-14
02-14-14
02-15-14
02-16-14
02-17-14
02-18-14
02-19-14
02-20-14
02-21-14
02-22-14
02-23-14
02-24-14
02-25-14
Always prefer [[ ]] over [ ] when it comes to conditional expressions in Bash. (( )) may also be a preference.
It requires GNU date. e.g. date --version = date (GNU coreutils) 8.21 ...
mm-dd-yy is not a format acceptable by date for input so I used yyyy-mm-dd which is acceptable.
Given a date in format 20130522, I need to generate a sequence of date+hour as below:
2013052112,2013052113,2013052114,...,2013052122,2013052123,
2013052200,2013052201,2013052202,...,2013052222,2013052223,
2013052300
in which the first date+hour is 12 hours before the given date and the last date+hour is the midnight in the next day of the given date.
I tried several ways but none of them is ideal. How to generate such a sequence in a clean way using shell script? Thanks!
--Edit--
Per your request, this is what I have so far:
day=20130522
begin=`date --date "$day -12 hours"`
begin=`date -d "${begin:0:8} ${begin:8:2}" +%s`
end=`date --date "$day +1 day"`
end=`date -d "${end:0:8} ${end:8:2}" +%s`
datestr=`date -d #${begin} +%Y%m%d%H`
let begin=$begin+3600
while [ $begin -le $end ]
do
hr=`date -d #${begin} +%Y%m%d%H`
datestr="$datestr,$hr"
let begin=$begin+3600
done
and this is what I got from above:
2013052100,2013052101,2013052102,...,2013052123,
2013052200,2013052201,2013052202,...,2013052223,
2013052300
You can use date and brace expansion:
date=20130522
echo $(date -d "-1 day $date" +%Y%m%d){12..23} \
"$date"{00..23} \
$(date -d "+1 day $date" +%Y%m%d)00
Output (wrapped):
2013052112 2013052113 2013052114 2013052115 2013052116 2013052117 2013052118 2013052119 2013052120
2013052121 2013052122 2013052123 2013052200 2013052201 2013052202 2013052203 2013052204 2013052205
2013052206 2013052207 2013052208 2013052209 2013052210 2013052211 2013052212 2013052213 2013052214
2013052215 2013052216 2013052217 2013052218 2013052219 2013052220 2013052221 2013052222 2013052223
2013052300
Your code was quite well. What I think is that you used so much bash conversion, while date is very powerful and handles in an easier way.
I rewrited something and now I get this:
day=20130522
begin=$(date --date "$day -12 hours" "+%s")
end=$(date --date "$day +1 day" "+%s")
hr=$(date --date "#$begin" "+%s")
while [[ $hr -lt $end ]]
do
hr=$(($hr + 3600))
echo $(date -d "#$hr" "+%Y%m%d %H")
done
$ ./script
20130521 13
20130521 14
.../...
20130522 22
20130522 23
20130523 00
I'd like to send a renewal reminder email out four weeks before an expiry date. I'm storing all the details in an array, but I can't figure out how I check if today's date is 28 days before the date in the array.
Here's what I've got so far, any help with how to do the date checking would be much appreciated:
#!/bin/sh
adminemail="me#gmail.com"
account[1]="June 03|john#gmail.com|John"
account[2]="April 17|jane#gmail.com|Jane"
account[3]="November 29|sarah#gmail.com|Sarah"
for check in "${account[#]}"
do
renew=$(echo $check | cut -f1 -d\|)
email=$(echo $check | cut -f2 -d\|)
name=$(echo $check | cut -f3 -d\|)
# check date is 28 days away
if [ ?????? ]
then
subject="Your account is due for renewal"
text="
Dear $name,
Your account is due for renewal by $renew. blah blah blah"
echo "$text" | mail -s "$subject" $email -- -r $adminemail
fi
done
You can get the month and date of 28 days before the check date like this:
warning_date=$(date --date='June 03 -28 days' +%s)
The current date in the same format:
current_date=$(date +%s)
Since they are both numeric and in the same scale (seconds since epoch), now you can check if $current_date is greater than $warning_date:
if [ $warning_date -lt $current_date ]; then
# ...
fi
Put it all together now:
# ...
current_date=$(date +%s)
for check in ${account[#]}; do
# ...
renew=$(echo $check | cut -f1 -d\|)
# 28 days before the account renewal date
warning_date=$(date --date="$renew -28 days" +%m%d)
if [ $warning_date -lt $current_date ]; then
# Set up your email and send it.
fi
done
Update
To be reminded only if current date is the 28th day prior to the check date you can get each date in the same month date format and compare for string equality:
# ...
current_date=$(date "+%B %d")
for check in ${account[#]}; do
# ...
renew=$(echo $check | cut -f1 -d\|)
# The 28th day before the account renewal day
warning_date=$(date --date="$renew -28 days" "%B %d")
if [ $warning_date == $current_date ]; then
# Set up your email and send it.
fi
done
It's best to use Unix timestamps for date comparisons, as they are simple integers.
#!/bin/bash
adminemail="me#gmail.com"
account[1]="June 03|john#gmail.com|John"
account[2]="April 17|jane#gmail.com|Jane"
account[3]="November 29|sarah#gmail.com|Sarah"
for check in "${account[#]}"
do
IFS="|" read renew email name <<< "$check"
# GNU date assumed. Similar commands are available for BSD date
ts=$( date +%s --date "$renew" )
now=$( date +%s )
(( ts < now )) && (( ts+=365*24*3600 )) # Check the upcoming date, not the previous
TMINUS_28_days=$(( ts - 28*24*3600 ))
TMINUS_29_days=$(( ts - 29*24*3600 ))
if (( TMINUS_29_days < now && now < TMINUS_28_days)); then
subject="Your account is due for renewal"
mail -s "$subject" "$email" -- -r "$adminemail" <<EOF
Dear $name,
Your account is due for renewal by $renew. blah blah blah
EOF
fi
done