I've got a text-file that's sorted by the first field, which is month/day (eg: 02-20)

I've been using grep on rhe text file, but that's easiest to do with today's date, using the date command in a script.

I'd like to pull up the prior 3 days, today's date, and the coming 5 days... but I don't know an easy way to handle the end of the month/into the next month. I could, I s'pose, hard code that in? But I was looking for an elegant solution to the problem, so I'm seeking suggestions.

I'd prefer to use regular commands so the script is as portable as possible.

ie: by portable, I mean not just limited to Linux/GNU machines (and/or having to install specific utilities in order to make everything run). Currently I think I'm on a BSD machine, and I've been on most flavors of UNIX (Solaris, blah, blah, blah) - and probably will be in the future.

I'd take something that's more convoluted to program/script, that uses very basic tools, over having to remember to install packages everytime I might be on a different flavor of UNIX (when I could just scp in my scripts directory and have it work).

FYI: Current date -d flag sets daylight savings time :)

 -d dst  Set the system's value for Daylight Saving Time.

Okay, I just gave an example (as asked) of the type on data in the file (it's much bigger, obvs), but I didn't give edge cases. Here's an edge case, ie: how do I handle the end of month period, without programming in a test for the end of the month, for each month (Or 4 types of month: 30, 31, 28 & 29).

01-01   Something To Do Here    Notes that may run for a really long time
02-05   On the 5th of the Month         Do this other thing, because of X
02-06   The day after           Do this cleanup stuff
03-15   The Ides                Remember, remember the Brutus of December
03-29   Edge                    Does this show up on the 1st of April?
03-30   Edge2                   Does this show up on the 1st and 2nd?
03-31   Last day in the month   Shows?
04-01   Edge                    Does this show on 31st?  Or does it only show 03-35?
  • 1
    Define portable. It is trivial to do this for any Linux system since GNU date has some nifty tricks but that won't work on non-Linux systems. It will, however, work on any GNU system, is that portable enough? Also, please edit your question and give us an example of your file so we know what we're dealing with.
    – terdon
    Jun 18, 2015 at 12:25

4 Answers 4


GNU date allows you to give strings to the -d option like -2 days and 5 days. The -f option allows these strings to be read from a file or pipe. So using GNU date and bash:

printf '%d days\n' {-2..5} | date -f - +%m-%d | grep -f - in_file


If tcl is acceptable, here is a script to print out the dates:


set now [clock seconds]

for {set n -2} {$n<=5} {incr n} {
  set out_date [clock add $now $n days]
  puts [clock format $out_date -format %m-%d]

Just pipe the output of this to grep -f - as above.

  • date on the current machine I'm working with is POSIX.1, and has no -f option [-adjrtz]
    – user3082
    Jun 18, 2015 at 14:55
  • @anon3202, if your date supports -d (note neither -d or -f are supported by POSIX), you might get away with using a loop - for n in {-2..5}; do date -d "$n days" +%m-%d; done | grep -f - in_file. Try it without the grep first to see if the dates get printed.
    – Graeme
    Jun 18, 2015 at 18:27
  • FYI: current date says "compliant with the IEEE Std 1003.1-2008" Also, everything but today's date says: "date: settimeofday: Operation not permitted"
    – user3082
    Jun 19, 2015 at 14:54
  • @anon3202, your date is POSIX compliant, but POSIX only requires that it have the -u option. Anything else is an extra. See the standard here - pubs.opengroup.org/onlinepubs/000095399/utilities/date.html. I should have said 'required' above, but instead I put 'supported'.
    – Graeme
    Jun 19, 2015 at 16:26
  • @anon3202, the problem you have is that the POSIX standard doesn't provide a way to do date/time arithmetic in the shell. This means that you need to either use something else that does eg perl/python/tcl or write your own date increment/decrement functions (using awk would probably be best). Rolling your own a bit much for an SE answer, but its not that hard to do.
    – Graeme
    Jun 19, 2015 at 16:51

Well here is a one liner that seems to do the job...

dt=$(date +%m-%d);for i in {-2..5}; do grep "$(echo "$dt"|awk -F"-" -v count=$i '{print $1"-0"$2+count}')" mtest; done

You can make this a function by adding this to your .bashrc ...

todolist(){  dt=$(date +%m-%d);for i in {-2..5}; do grep "$(echo "$dt"|awk -F"-" -v count=$i '{print $1"-0"$2+count}')" $1; done; }

and then you just call it

todolist whateverthefilename.txt

It isn't very pretty but I tested it with your data it did work.

$ echo -e "01-01   Something To Do Here    Notes that may run for a really long time\n02-05   On the 5th of the Month         Do this other thing,\nbecause of X\n02-06   The day after           Do this cleanup stuff\n03-15   The Ides                Remember, remember the Brutus of December">> mtest

$ dt=02-05; for i in {-2..5}; do grep "$(echo "$dt"|awk -F"-" -v count=$i '{print $1"-0"$2+count}')" mtest; done
02-05   On the 5th of the Month         Do this other thing,
02-06   The day after           Do this cleanup stuff
  • Why hardcode a zero into the day? You will get the correct date strings only 2 days a month!
    – Graeme
    Jun 18, 2015 at 19:41
  • @Graeme I missed that first time around. I was working only with your data. The zero is there because awk returns 5 when 04+1 is evaluated. My mistake.
    – rcjohnson
    Jun 19, 2015 at 12:21

You can use touch to POSIXLY test the validity of a date string.

I'll use a couple options here.

  • -c
    • Do not create a specified file if it does not exist. Do not write any diagnostic messages concerning this condition.
  • -t

    • Use the specified time instead of the current time. The option-argument shall be a decimal number of the form:

    • If the time cannot be represented as the file's timestamp, touch shall exit immediately with an error status.

    • Note that the [[CC]YY] fields are optional.

And so, for example:

        if      command -v touch >&2
        then    touch -c ''
                eval '  touch -ct"$1" ""
                        [ "$?" = '"$? ]"
        else    { touch; } 2>&3
        fi      3>&2 2>/dev/null

It's pretty careful to handle matters portably, I think. In tests with both a GNU and an heirloom (Solaris) touch, the GNU tool returns 1 for a bad date and 0 for a good one, but the heirloom version returns 2 and 1 respectively. I'm fairly sure that the old Solaris tool's behavior is specifically referenced in the spec's rationale where I find:

  • At least one historical implementation of touch incremented the exit code if -c was specified and the file did not exist. This volume of POSIX.1-2008 requires exit status zero if no errors occur.

I am more convinced when I compare the behavior of different versions of my heirloom tools. The one from the base set - which is designed to behave as much like the original set as is possible and still be useful on modern systems - is the one that returns the 2 and 1 as mentioned. However, when I use the /usr/heirloom/bin/posix2001/touch executable - which is the version which has been modified for standards compliance as necessary - it returns exactly as does the GNU tool.

In any case, the function handles both the old Solaris, the modified-for-compliance Solaris, and the GNU behaviors, because it compares the results of an intial call to touch on a ''null file operand (which definitely does not exist) against a second call on same and returns 0 for a good date or 1 for a bad one. It returns 127 and writes to stderr if touch cannot be found.

Here's how you can use it:

testtime MMDD0000 && echo good date || echo bad date

...where MM and DD are 2-digit decimal strings representing the date you wish to test. You can also check for valid times that way - and even with a .S seconds field.


I posted my first answer yesterday with way too little sleep. I am back and ready to try again. Rather than try to awk the dates I decided I was being too clever so I went back and rethought it.

todolist(){ for i in {-2..5}; do grep "$(date -d "${date} +$i days" +%m-%d)" $1 ; done; };

Just add the one-liner to your .bashrc and then feed it the file.

This time I let date do all the work. I got the hint from here. This little bit will add any number of days from today's date and output it in the format you need for your file...

date -d "${date} +$i days" +%m-%d)"

The function above should now work without resorting to hard-coded values.

  • Except, I'm not adding anything to a file, I'm trying to specify what I'm extracting from a file. File exists, but I only want a subset (what should've been done, and what needs to get done soon) out of it. Not things that need to be done 6 months from now.
    – user3082
    Jun 19, 2015 at 15:01
  • This isn't much different in essence from the first answer I gave. The issue is that the +n days syntax with date is not portable. AFAIK it is specific to GNU date. POSIX doesn't even require that date has a -d option - see pubs.opengroup.org/onlinepubs/9699919799/utilities/date.html
    – Graeme
    Jun 19, 2015 at 16:57
  • @anon3202 This method (and the one initially given by Graeme) will pull out only the things due on "today's date, 2 days previous to and 5 days later. It is not pulling things 6 months from the date. Of course Graeme's point about whether this will work for you is still valid.
    – rcjohnson
    Jun 19, 2015 at 18:58
  • @anon3202 Try this to test your date command--- for i in {-2..5}; do echo "$(date -d "${date} +$i days" +%m-%d)" $1 ; done --- you should get a list of 8 days
    – rcjohnson
    Jun 19, 2015 at 19:06
  • results: 3 of today's date (duplicates), and 5 'settimeofday: operation not permitted'. rcjohnson: I was replying to: "output it in the format you need for your file" - which I don't need. Not adding anything to the file.
    – user3082
    Jun 28, 2015 at 20:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .