Can cron do this?

Can cron run a script on the 4th Saturday of the month, every month? I don't believe it can.

Here's what I have so far:

30 5 24-31 * 6 <command>

The syntax you show really should work, but it doesn't. It will run your script on each day of 24 through 31 and on each Saturday. This interpretation of the cron fields arose in System V and everyone who had right conformed.

Your math is off though. The 4th Saturday must occur on the 22 through the 28.

Try this:
30 5 * * 6 [ `date +%e` -gt 21 -a `date +%e` -lt 29 ] && <command>

I did as you suggested but get this error:

sh: test: 0403-021 A ] character is missing.

Here is my syntax (modified for testing today):

57 13 * * 4 [ `date +%e` -gt 8 -a `date +%e` -lt 11 ] && mail -s "cron test" me@homer.com < /users/me/test.txt

Any ideas?

I have actually put code in a crontab and I too am getting the same error. That is odd since we have some very similiar tests in working crontabs. I don't have time to dig into now, but I will soon. In the meantime you could write a script called week4 that returns 0 or 1 depend on the week. And then use
week4 && command

This has actually exposed a very interesting problem. Right now I'm thinking that cron jobs are pumped through a proto file just like at jobs.

There seem to be two issues: first everything after a per cent sign is getting truncated. You would think that I would have encountered a cron command before that used a %, but I guess that this must be my first. Also the backtick expressions are not being evaluated.

The syntax that works for me is:

          • eval [ `date +\%e` -gt 8 -a `date +\%` -lt 11] && <command>

This is ugly enough that some cool script is needed. I'll write one this weekend.

Excuse my continuing monologue :slight_smile:

Anything after a % is stripped off a cron command and is feed as standard input into the command. This is mentioned on crontab(1) manpage. SunOS even has an example on their manpage.

Thanks. I will test and advise.

Edit: This is version 2.0. One thing version 1.0 could not handle is running something every other week. This version handles that and it now requires my datecalc script. You may need to adjust the alias if you install datecalc somewhere other than /usr/local/bin.

To understand to even/odd stuff: datecalc -j can return a day number for a given month day and year. And datecalc -d can return a numeric day-of-week. Subtract the second from the first and you get the day number of the sunday that starts the week. Divide that by 7 and you get a week number. Now we just need to test that for even or odd.

And now the code...

#! /usr/bin/ksh
alias datecalc=/usr/local/bin/datecalc

#  weekselector ---  script to assist in scheduling cron jobs
#  Perderabo  Oct 11, 2003
#
#  example:
#  You want to run command1 on the first monday of each month and
#  you want to run command2 on the last monday of each month.
#  Just use this syntax:
#
#    0 0 * * 1 /usr/local/bin/weekselector 1st && command1
#    0 0 * * 1 /usr/local/bin/weekselector last && command2
#
#  Version 2.0  March 23, 2005
#
#  You can now use "even" or "odd" to run commands every other week.
#  Whether or not a week is "even" or "odd" is arbitrary, but if one week
#  is "odd", the next will be "even".  An "even" Monday will be in the same
#  week as an "even" Tuesday.

integer month day year first last leap

set -A mdy $(date "+%m %d %Y")
month=${mdy[0]}
day=${mdy[1]}
year=${mdy[2]}
#date "+%M %d %Y"  | read month day year

case $1 in
    even|Even|EVEN|odd|Odd|ODD)
            integer dow mjd fmjd wk
            dow=$(datecalc -d $year $month $day)
            mjd=$(datecalc -j $year $month $day)
            ((fmjd=mjd-dow))
            ((wk=fmjd/7))
            ((wk=wk+${#1}))
            ((wk/2*2 == wk))
            exit $?
            ;;
    1|1st|1ST|first|First|FIRST)    last=7  ;;
    2|2nd|2ND|second|Second|SECOND) last=14 ;;
    3|3rd|3RD|third|Third|THIRD)    last=21 ;;
    4|4th|4TH|fourth|Fourth|FOURTH) last=28 ;;
    last|Last|LAST)
            #                 ja fe ma ap ma jn jl ag se oc no de
            set -A mlength xx 31 28 31 30 31 30 31 31 30 31 30 31

            if ((month != 2)) ; then
                last=${mlength[month]}
            else
                leap=0
                if ((!(year%100))); then
                        ((!(year%400))) && leap=1
                else
                        ((!(year%4))) && leap=1
                fi

                last=28
                ((leap)) && last=29
            fi;;
    *) exit 1;;
esac
((first=last-6))

((first<=day && day<=last))
exit $?
2 Likes

Incredible Perderabo!

Simply incredible and very helpful to others.

Warmest Regards, Neo