6

I want to store all possible dates between two user input dates. The input will be as like 20140605 and 20140830.

I want to store every date between the given dates into a variable using a loop so that I can use it.

The date generated or calculated must be in the same format as the input.

startdate=20141030
starttime=165800

enddate=20141120
endtime=175050

day=`expr $startdate % 100`
month=`expr $startdate % 10000`
year=`expr $startdate % 100000000`
month=`expr $month / 100`
year=`expr $year / 10000`
echo "$year  $month  $day"

while [ $enddate -ge $cdate ]
do
var=$cdate

#using variable var

if  [ $day -eq 31 ]; then
cdate=`expr $cdate - $day`
day=1
((month++))
cdate=`expr $cdate + 100 + $day`
#cdate=`expr $cdate + $day`
    if  [ $month -eq 13 ]; then
        #tmp=`expr $month \* 100`
        cdate=`expr $cdate - $month \* 100`
        month=1
        ((year++))
        cdate=`expr $cdate + 10100`
        if [ $year -eq 2999 ]; then
            ((year++))
            echo $cdate
            cdate=30010100
        fi
    fi
else
    ((day++))
    ((cdate++)) 
fi

if [ $enddate == $cdate ]; then
check=1
fi
done

I have tried to implement my requirement this way. But on compilation it says:

unary operator expected

What is the cause of this error, and how can I do this in a better way using a shell script?

Mat
  • 52,586
  • What shell do you use, Bash? Does the error give a line number? – drs Jul 07 '14 at 12:00
  • When you say all dates, can we assume you mean all days? All hours? All minutes? What do you mean "on compilation"? Are you somehow compiling your shell script into a binary? Why do you need to store these in a variable? It would be simpler to check whether a given date falls inside your range. – terdon Jul 07 '14 at 12:06
  • @drs Yes I am using bash. The while loop condition statement gives an error and other unkniwn line gives expr: syntax error – FredOscatore Jul 07 '14 at 12:21
  • @terdon Yes you can assume all minutes and seconds. No I am not compiling it into binary. I need to store the date into a variable because I want to use that variable into find command to find the files having those dates on their filename. – FredOscatore Jul 07 '14 at 12:24
  • 1
    Every second? That can be huge and it is a horribly complex way of doing it; why not specify a date range and give that to find directly? I don't even understand how you could possible pass your shell variable to find short of running a separate find call for each of the millions of seconds between your dates. – terdon Jul 07 '14 at 12:40
  • What @terdon said but if you really need to do this convert your datetime into seconds using the date command, increment the seconds value and then convert back again to yyyymmd hh:mm:ss, again using the date command. That will get rid of all that crazy arithmetic. I can post some code if that will help. – Keith Miller Jul 07 '14 at 12:51
  • This error frequently occurs in the test block of an if/which statement, when a variable has not been set. Lookin at the code, I can't see where the variable $cdate is initialised. Are you missing, perhaps: cdate=$startdate ? – robhogg Jul 07 '14 at 12:49
  • Some filesystems store filesystem with a finer granularity than 1s. For example ext4 on Linux has 1ns granularity. You won't be able to list all possible nanosecond values. Fortunately, it's completely pointless, as terdon has explained. – Gilles 'SO- stop being evil' Jul 07 '14 at 22:06
  • @robhogg You pointed the correct error which I had failed to notice. – FredOscatore Jul 08 '14 at 04:32
  • @KeithMiller Here is the link to my basic problem link – FredOscatore Jul 08 '14 at 04:37
  • @Gilles Kindly see this Link – FredOscatore Jul 08 '14 at 04:41

2 Answers2

12

I would take a completely different approach to this. Doing all the date calculations by hand is error prone (there aren't always 31 days in a month, you have leap years, etc). Plus skimming through your code, it's very hard to tell what you're doing (which is a very bad thing).

#!/bin/bash
startdate=20141030
enddate=20141120

dates=()
for (( date="$startdate"; date != enddate; )); do
    dates+=( "$date" )
    date="$(date --date="$date + 1 days" +'%Y%m%d')"
done
echo "${dates[@]}"

This uses the date command to handle all computations, storing each date in the $dates array.

The result looks like this:

20141030 20141031 20141101 20141102 20141103 20141104 20141105 20141106 20141107 20141108 20141109 20141110 20141111 20141112 20141113 20141114 20141115 20141116 20141117 20141118 20141119
phemmer
  • 71,831
3

If, as you explained in your comments, your end goal here is to find all files modified within a given date range, then saving said range into a variable is pointless. Instead, you can give the range to find directly:

find . -mtime $start -mtime $end

Personally, I would use a different approach. Just create two temp files, one created a second before your start date and one created a second after your end date. Then use GNU find's -newer test to find files that are newer than the first and not newer than the latter.

The tricky bit is getting $start and $end correctly. You could try something like:

#!/usr/bin/env bash

## If you really want to use this format, 
## then you must be consistent and always 
## us YYYYMMDD and HHMMSS.
startdate=20141030
starttime=165800

enddate=20141120
endtime=175050

## Get the start and end times in a format that GNU date can understand
startdate="$startdate $( sed -r 's/(..)(..)(..)/\1:\2:\3/' <<<$starttime)"
enddate="$enddate $( sed -r 's/(..)(..)(..)/\1:\2:\3/' <<<$endtime)"

## GNU find has the -newer test which lets you find files
## that are newer than the target file. We can use this
## and create two temporary files with the right dates.
tmp_start=$(mktemp)
tmp_end=$(mktemp)

## Now we need a date that is one seond before the
## start date and one second after the end date.
## We can then use touch and date to set the creation date 
## of the temp files to these dates. 
minusone=$(date -d "$startdate -1 sec" +%s)
plusone=$(date -d "$enddate +1 sec" +%s)

## Set the creation times of the temp files.
## The @ is needed when using seconds since
## the epoch as a date string.
touch -d "@$minusone" $tmp_start
touch -d "@$plusone" $tmp_end

## At this point we have two files, tmp_start and
## tmp_end with a creation date of $startdate-1 
## and $enddate+1 respectively. We can now search
## for files that are newer than one and not newer
## than the other.
find . -newer $tmp_start -not -newer $tmp_end

## Remove the temp files
rm $tmp_start $tmp_end
terdon
  • 242,166
  • You misunderstood me, The Date is in the Filename and is not the last modified or access date. Btw Thanks for your effort and help. :) – FredOscatore Jul 08 '14 at 04:04