Bash: start date less than equal to end date - bash

#!/bin/bash
# slightly malformed input data
input_start=2014-11-1
input_end=2016-01-1
# After this, startdate and enddate will be valid ISO 8601 dates,
# or the script will have aborted when it encountered unparseable data
# such as input_end=abcd
startdate=$(date -I -d "$input_start") || exit -1
enddate=$(date -I -d "$input_end") || exit -1
m="$startdate"
while [ "$m" != "$enddate" ]; do
echo $m
m=$(date -I -d "$m + 1 month")
done
"Script is running fine but when I want to change the While loop condition i.e '<=' 'less then or equal to' its giving error even I tried using "-le".What I want to do here is startdate <= enddate in while loop. Can anyone suggest what needs to done to overcome this issue.
Same Code

-le is for numeric data. 2014-11-01 is not a number. Use < or >. (You need to escape them as \< or \>. Or use [[ instead of [.)
effectively, change
while [ "$m" != "$enddate" ]; do
to
until [ "$m" \> "$enddate" ]; do
or
until [ "$m" '>' "$enddate" ]; do
or
until [[ "$m" > "$enddate" ]]; do
Alternately, use seconds since epoch instead of ISO8601 format.
while [ "$(date -d "$m" +%s)" -le "$(date -d "$enddate" +%s)" ]; do

With dateutils' datetest this is simple:
$ datetest 2014-11-1 --le 2016-01-1 ; echo $?
0
$ datetest 2014-11-1 --gt 2016-01-1 ; echo $?
1
Then again, what you want is simply done by dateseq, which also happens to be a tool of the dateutils suite.
$ dateseq 2014-11-1 +1mo 2016-01-1
2014-11-01
2014-12-01
2015-01-01
2015-02-01
2015-03-01
2015-04-01
2015-05-01
2015-06-01
2015-07-01
2015-08-01
2015-09-01
2015-10-01
2015-11-01
2015-12-01
2016-01-01
Disclaimer: I am the author of the package.

Related

Conditional statement when it comes to date format

I have to validate the inputted date from the user. The accepted date format is '+%m/%d/%Y' (04/10/1999)
I also have to validate if the 1st parameter is greater than or equal to 2nd parameter which is also a date. For example: 10/04/2000 10/04/1999, this will echo '1st should be greater than 2nd'
`
#operational statement
if [ "$1" -gt "$2" ]; then
echo "1st should be greater than 2nd"
exit 255
fi #having error of integer expression expected
#validate inputted date
if [ "$1" -ne `date -d '+%m/%d/%Y'` ] || [ "$2" -ne `date -d '+%m/%d/%Y'` ];
then
echo "Invalid. Follow [MM/DD/YY] Format"
exit 255
fi #i think there is a mistake with my condition but can't figure it out.
#!/bin/sh
# validation
for date do
case $date in
[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]);;
*) echo "$date: invalid, must be MM/DD/YYYY" >&2; die=1
esac
done
[ "$die" ] && exit 1
# sort dates (new to old)
printf '%s\n' "$1" "$2" |
sort -rt / -k 3,3 -k 1,2 |
sed '1ais newer than'
Or instead of sort, arrange units largest to smallest, remove non-digits, and compare:
printf '%s\n' "$1" "$2" |
{ IFS=/
read m1 d1 y1
read m2 d2 y2
if [ "$y1$m1$d1" -gt "$y2$m2$d2" ]; then
echo "$1 is newer than $2"
else
echo "$2 is newer than $1"
fi; }
The question isn't tagged bash, so this is POSIX shell. If you can control the date format, you could choose YYYYMMDD, so they can be directly compared ([ "$1" -gt "$2" ]).

bash automate the user input <enter> in the loop

#!/bin/sh
dom=$(date '+%d') # 01-31, day of month
year=$(date '+%Y') # four-digit year
month=$(date '+%m') # two-digit month
nworkdays=0
for d in $( seq 1 $dom)
do
today=$(date -d "$year-$month-$d" '+%x') # locale's date representation (e.g. 12/31/99)
dow=$(date -d "$year-$month-$d" '+%u') # day of week: 1-7 with 1=Monday, 7=Sunday
if [ "$dow" -le 5 ] && grep -vq "$today"
then
workday=Yes
nworkdays=$((nworkdays+1))
else
workday=
fi
done
[ "$workday" ] && [ "$nworkdays" -eq 6 ] && echo "$nworkdays $today $workday"
~
I wanted to execute the script but the script goes inside the loop and asks for user input.
Expected result
on execution display workday
Best Regards,
Shalini
As mentioned by #urbanespacemen kindly remove the grep command
#!/bin/sh
dom=$(date '+%d') # 01-31, day of month
year=$(date '+%Y') # four-digit year
month=$(date '+%m') # two-digit month
nworkdays=0
for d in $( seq 1 $dom)
do
today=$(date -d "$year-$month-$d" '+%x') # locale's date representation (e.g. 12/31/99)
dow=$(date -d "$year-$month-$d" '+%u') # day of week: 1-7 with 1=Monday, 7=Sunday
if [ "$dow" -le 5 ]
then
workday=Yes
nworkdays=$((nworkdays+1))
else
workday=
fi
done
[ "$workday" ] && [ "$nworkdays" -eq 6 ] && echo "$nworkdays $today $workday"
Now should work

shell script for date between 2 different dates

I'm struggling a bit on how to put the correct date in my shell script for a specific time range.
For instance i need to check if i'm in the correct range to generate some file based on date(12am - 3am )
so far I'm checking :
workflow_start_time=$(date -d "$(date +%Y-%m-%d) 23:00:00" +"%s")
scheduled_run_time=$workflow_start_time
if [ "$(date -d #${scheduled_run_time} +"%H")" -ge 23 ] && [ "$(date -d #${scheduled_run_time} +"%H")" -lt 2 ]; then
that is starting at 23 because i need to take into account 1 hour server difference, so this will start around 12 and should finish around 2am, which will be 3am.
any help here?
Convert date to timestamp.
toStamp() { date --date="$1" +%s; }
Check if date is between other dates.
# [$1, $2): bounds
# $3 current date
isBetween() {
[[ `toStamp $1` -le `toStamp $3` ]]&&\
[[ `toStamp $2` -gt `toStamp $3` ]]
}
or
isTimeStampBetween() {
[[ $1 -le $3 ]]&&\
[[ $2 -gt $3 ]]
}
if you have timestamps only.
An example to check if a date is between 2 other would be:
if $(isBetween 2012 2020 2013) ; then dosomething
You can include these functions in your file.

Bash Script for deleting old backup files

every night my server creates backups for every mysql database. All of these files are saved in a folder like /backup/mysql/2014-11-28. Over the last few months a lot of folders and files have been stored in that directory and I would like to reduce this.
Therefore I would need a bash script which deletes every folder in the given directory, except every folder created this month (not in the last 30 days, but the actual month) and except one backup from every week (for example the backup from sunday). Since I have no clue how to do the sunday party I decided it would be simpler to keep the backups from the 07th, the 14th, the 21st and the the 28th.
#!/bin/bash
in_array() {
local haystack=${1}[#]
local needle=${2}
for i in ${!haystack}; do
if [[ ${i} == ${needle} ]]; then
return 0
fi
done
return 1
}
YEAR=`date +%Y`
LASTYEAR=`date +%Y -d "1 year ago"`
MONTH=`date +%m`
DAYS="07 14 21 28"
for i in $( ls ); do
BACKUP_YEAR=$(echo "${i}" | cut -d'-' -f1)
BACKUP_MONTH=$(echo "${i}" | cut -d'-' -f2)
BACKUP_DAY=$(echo "${i}" | cut -d'-' -f3)
DELETE=false
if [[ "$BACKUP_YEAR" == "$YEAR" ]]; then
if [[ "$BACKUP_MONTH" != "$MONTH" ]]; then
if [ $(in_array $DAYS "$BACKUP_DAY") ]; then
DELETE=true
fi
fi
fi
if [[ "$BACKUP_YEAR" == "$LASTYEAR" ]]; then
if [[ "$BACKUP_DAY" != ${DAYS[0]} ]]; then
DELETE=true
fi
fi
if [ "$DELETE" = true ] ; then
#OUTPUT=`rm -v -R $i`
echo $i
fi
done
The second part (backups from the year before) works perfectly fine, but the first part (for backups from this year) doesn't work and I don't know why. I found the function on this site, but I guess somehow my call is wrong.
EDIT: The code I am now using:
#!/bin/bash
read YEAR MONTH <<<$(date "+%Y %m")
LASTYEAR=$(( YEAR-1 ))
DAYS=" 07 14 21 28 "
for fn in $( ls )
do
if ([ "${fn:0:4}" = "$YEAR" ] &&
[ "${fn:5:2}" != "$MONTH" ] &&
[ "${DAYS/ ${fn:8:2} /}" = "$DAYS" ]) || ([ "${fn:0:4}" = "$LASTYEAR" ] &&
[ "${fn:8:2}" != ${DAYS:1:2} ])
then
#OUTPUT=`rm -v -R $fn`
echo "$fn"
fi
done
#!/bin/bash
read YEAR MONTH <<<$(date "+%Y %m")
LASTYEAR=$(( YEAR-1 ))
DAYS=" 07 14 21 28 "
for fn in $( ls )
do
DELETE=false
if [ "${fn:0:7}" = "$YEAR-$MONTH" ] &&
[ "${DAYS/ ${fn:8:2} /}" != "$DAYS" ]
then
DELETE=true
elif [ "${fn:0:4}" = "$LASTYEAR" ] &&
[ "${fn:8:2}" != ${DAYS:1:2} ]
then
DELETE=true
fi
if [ "$DELETE" = true ]
then
#OUTPUT=`rm -v -R $fn`
echo "$fn"
fi
done
Assuming that a file name matches the following date format YYYY-MM-DD, for instance, 2014-03-27, the expression ${fn:0:7} would be 2014-03; ${fn:8:2} would be 27; ${fn:0:4} would be 2014.
Read the bash manual pages about the parameter expansion.
The expression ${DAYS/ ${fn:8:2} /} checks the day from the filename (wrapped by white spaces) is found in the DAYS list. It would be equivalent to echo "${DAYS}" | grep -q "${BACKUP_DAY}".
note: it is possible to reduce the commands inside the for loop (join if and elif by means of a || and remove the DELETE variable checking and the DELETE variable itself) but I decided to keep the look of the original script.

Shell script that calculates the previous Monday, Tuesday,... relative to today

I'm trying desperatly to find a bash or ksh routine that allows me to find for example the previous Monday,Tuesday,Wednesday,... preceding today's date. Additonal it has to work on plain vanilla Solaris X and I don't have the GNU date available.
eg: Today = Thursday 2013/01/17 ; Let's say I want to find the last Monday. It has to return:
2013/01/14
I've managed to find a script on the net that does the job perfectly for all days except in this specific case:
eg: Today = Thursday 2013/01/17 ; I want to find the last Thursday which should give as result: 2013/01/10 ; but instead I get todays date again.
The script used was this:
#!/bin/ksh
#Get the nbr of the current weekday (1-7)
DATEWEEK=`date +"%u"`
#Which previous weekday will we need (1-7)
WEEKDAY=$1
# Main part
#Get current date
DAY=`date +"%d"`
MONTH=`date +"%m"`
YEAR=`date +"%Y"`
#Loop trough the dates in the past
COUNTER=0
if [[ $DATEWEEK -eq $WEEKDAY ]] ; then
# I need to do something special for the cases when I want to find the date of the same day last week
DAYS_BACK=168
DAY=`TZ=CST+$DAYS_BACK date +%d`
echo "DAY (eq) = $DAY"
else
while [[ $DATEWEEK -ne $WEEKDAY ]] ; do
COUNTER=`expr $COUNTER + 1`
echo "Counter is: $COUNTER"
DAYS_BACK=`expr $COUNTER \* 24`
echo "DAYS BACK is: $DAYS_BACK"
DAY=`TZ=CST+$DAYS_BACK date +%d`
echo "DAY is: $DAY"
if [[ "$DAY" -eq 0 ]] ; then
MONTH=`expr "$MONTH" - 1`
if [[ "$MONTH" -eq 0 ]] ; then
MONTH=12
YEAR=`expr "$YEAR" - 1`
fi
fi
DATEWEEK=`expr $DATEWEEK - 1`
if [[ $DATEWEEK -eq 0 ]]; then
DATEWEEK=7
fi
done
fi
echo $DAY/$MONTH/$YEAR
(My previous suggestion didn't work as I thought. It was late last night ...)
The key thing is to ignore today, but still loop. Try it like this:
#!/bin/ksh
#Get the nbr of the current weekday (1-7)
DATEWEEK=`date +"%u"`
#Which previous weekday will we need (1-7)
WEEKDAY=$1
# Main part
#Get current date
DAY=`date +"%d"`
MONTH=`date +"%m"`
YEAR=`date +"%Y"`
#Loop trough the dates in the past
COUNTER=0
while [ $COUNTER -eq 0 ] || [[ $DATEWEEK -ne $WEEKDAY ]] ; do
COUNTER=`expr $COUNTER + 1`
echo "Counter is: $COUNTER"
DAYS_BACK=`expr $COUNTER \* 24`
echo "DAYS BACK is: $DAYS_BACK"
DAY=`TZ=CST+$DAYS_BACK date +%d`
echo "DAY is: $DAY"
if [[ "$DAY" -eq 0 ]] ; then
MONTH=`expr "$MONTH" - 1`
if [[ "$MONTH" -eq 0 ]] ; then
MONTH=12
YEAR=`expr "$YEAR" - 1`
fi
fi
DATEWEEK=`expr $DATEWEEK - 1`
if [[ $DATEWEEK -eq 0 ]]; then
DATEWEEK=7
fi
done
echo $DAY/$MONTH/$YEAR
Your DAY=`TZ=CST+$DAYS_BACK date +%d` trick doesn't work for me though. Linux date seems to cap it at one day.
Does this work?
today=$(date +"%u")
weekday=$1
curdate=$(date +"%s")
gobackdays=$(($today - $weekday))
if [ $gobackdays -le 0 ]; then
let gobackdays+=7
fi
SECSDAY=86400
backtime=$(($curdate - $gobackdays * $SECSDAY))
echo $(date -d "#$backtime")

Resources