I have a scheduled.txt file like this:

# Scheduled tasks for todo.txt
# Format:
#
# a     dow,        Mon,Tue,Wed,...
# b     moy,        Jan,Feb,Mar,...
# d     dom,        01..31
# D     m/d/y       12/30/12
# F     Y-m-d       2012-12-30
# j     doy         001..366
# V     woy         01..53
#
# %2 = Division by 2 is possible
# %N2= Devision by 2 is not possible
# 
# Empy lines are ignored

#===Planned===#
V=%2 a=Sun        ~Clean bed sheets
# Every odd day
j=%N2             ~Check wait
# Every 3 days
j=%3              ~Get finance up to date
# First of the month
d=01              ~Urenlijsten

This file is parsed by this script:

#!/bin/bash
# 2 modes: debug || commit

if [[ "x$1" = "xdebug" ]]; then
  COMMIT=false
  say(){ echo $*; }
elif [[ "x$1" = "xcommit" ]]; then
  COMMIT=true
  say(){ return; }
else
  echo 'debug or commit?'
  exit 1
fi

SCHEDFILE=$HOME/todo/repeatedtasks.txt
TODOFILE=$HOME/todo/todo.txt
TEMPTODOFILE=`mktemp`

# We work in a temporary file. Copying back happens at the end.
[ $COMMIT = 'true' ] && cp $TODOFILE $TEMPTODOFILE

addtask(){
  TASK=$*
  say "ADDING $TASK"

  # Add if commit is true:
  if [ $COMMIT = 'true'  ] ; then
    # We don't add tasks multiple times. If it's already there, append !
    if grep -q "^$TASK" $TEMPTODOFILE ; then
      # There already, append !
      sed -i "/$TASK/ s/$/\!/" $TEMPTODOFILE
    else
      # Not there already
      echo $TASK >> $TEMPTODOFILE

    fi
  fi
}

# Process each line in todo.txt
egrep "^[a-zA-Z]+" $SCHEDFILE | while read SCHED ; do
  say
  say "==== Parsing $SCHED ===="
  TASK=$(echo $SCHED | cut -d\~ -f2 )

  # We can have multiple date parts. They must all be true before a task is added.

  DATEPARTS=$(echo $SCHED | cut -d\~ -f1 | sed 's/ *$//g')
  say "The dateparts are [$DATEPARTS]"
  ADDME=no
  for DATEPART in $DATEPARTS; do
    # Parse this part
    # The parts of the line
    DATESTR=$(echo $DATEPART | cut -d= -f1 )
    DATEVAL=$(echo $DATEPART | cut -d= -f2 )
    say "Part $DATESTR = $DATEVAL"
   # Find the current date value for DATESTR
    DATESTR_FORMATTED=''
    for str in $DATESTR; do DATESTR_FORMATTED+="$str"; done
    CURDATEVAL=$(date +%$DATESTR_FORMATTED)

    say ".  STR [$DATESTR_FORMATTED] is now [$CURDATEVAL], we match on [$DATEVAL]. "

    # Process rules

    if [[ $( echo $DATEVAL | egrep -o "^%" ) = '%' ]]; then
      # check for division (eg every .. weeks/days/..)
      say ".  Checking [$CURDATEVAL] for division"

      # Actual checking
      NUMBER=$(echo $DATEVAL | egrep -o "[0-9]+")
      if [[ ! $(echo $DATEVAL | egrep -q "^%N" ) ]] ; then
        if [[   $(( $CURDATEVAL % $NUMBER )) -eq 0 ]]; then ADDME=yes ; else ADDME=no; break; fi
      else
        if [[ ! $(( $CURDATEVAL % $NUMBER )) -eq 0 ]]; then ADDME=yes ; else ADDME=no; break; fi
      fi
    else
      # Exact value
      say ".  Checking for exact"
      if [[ x$CURDATEVAL = x$DATEVAL ]] ;                           then ADDME=yes ; else ADDME=no; break; fi
    fi

    say "Passed tests. Addme is still $ADDME"

  done
  say "Exiting loop dateparts. ADDME=$ADDME"
  # All parts are parsed
  if [[ "x$ADDME" = "xyes" ]] ; then
    addtask $TASK
  fi

done

[ $COMMIT = 'true' ] && sort $TEMPTODOFILE > $TODOFILE
[ $COMMIT = 'true' ] && rm   $TEMPTODOFILE


Questions are welcome at the ‘mail’ account on this domain.

By karlo