2

I want to know whether there is any easier way to run a job every 20 days. In cron job, if you specify the day parameter as */20, it'll run only on 20th day of each month, IE basically becomes once every month.

The trick from CronJob every 25 minutes work for minutes but cannot apply to day I'm afraid, unless I'm running the cron job every day to check.

That'll be my last resort, but I'm wondering if there is any better/clever way to do it.

More info

This is for renewing purpose, thus can be done sooner than 20 days. But I'm doing it from my laptop, which would be put into sleep most of the time. Thus guarantee execution is a must (which I read that cron is lack of).

xpt
  • 1,530
  • 1
    @EduardoTrápani, this will be hardly applied here as some months have 28, some 30 and some 31 days. – Romeo Ninov Apr 26 '23 at 19:49
  • 1
    Hmm... if no good alternatives, I think I'd better do it every 15 days. It's a overkill, but at least the overall solution is simpler. UPDATE, maybe the alternative solution of systemd from https://unix.stackexchange.com/a/442864/374303 is the answer. Can someone confirm it please (even if my machine is rebooted every other week)? – xpt Apr 26 '23 at 19:58
  • @xpt, just few lines in the script, check the answer below :) – Romeo Ninov Apr 26 '23 at 20:05
  • Running a date check once a day is hardly a reason to exclude the simplest and clearest solution. At least it is testable on a scale of days, whereas a complex solution may have edge cases that occur once a year. And no solution is going to run on a hibernating system. You might check man anacron. – Paul_Pedant Apr 26 '23 at 20:48
  • @xpt I agree - a systemd timer unit with a combination of OnCalendar= (to set the first occurrence) and OnUnitActiveSec=20d perhaps – steeldriver Apr 26 '23 at 21:12
  • Great @steeldriver, it would even work when the scheduled time passes by during my system went in sleeping mode, right, after it wakes up? – xpt Apr 26 '23 at 21:20
  • @xpt tbh I'm not sure - the systemd.timer documentation mentions a Persistent= boolean but I don't have enough experience to whether it would work the way you want – steeldriver Apr 26 '23 at 21:32

2 Answers2

1

The only possible way to run the script every 20 day is to run it in cron every day and check in script if the number of days from start of the year confirm your requirement. Something like can do the work:

if [ $( expr $(date +%j) % 20) -ne 0 ]
then exit # days do not divide to 20 w/o remainder
fi
#your script here
Romeo Ninov
  • 17,484
  • 2
    Of course, there will be a 25-day interval between day 360 of each year and day 20 of the next (26-day if the outgoing year is a leap year). To be strictly regular, you would need to calculate in days since the Epoch. – Paul_Pedant Apr 26 '23 at 20:40
  • @Paul_Pedant, correct. Or some years the interval will be 26 days :) – Romeo Ninov Apr 27 '23 at 04:59
1

Here's how I would approach the problem

  1. Call your script from cron daily
  2. Near the top of the script check a state file to see if the last time it ran was 20 or more days ago
  3. If not then exit
  4. At the end of the script update the state file to reflect the current date/time

There are enhancements such as capturing the date/time when the script starts but only recording it when the script is about to exit successfully. Particularly relevant for a long-running process but less so for something that takes just moments to run.

I tend to use a last-run state file such as "$HOME/${0##*/}.lastran". Write date into it, but rely on the file modification date/time rather then its contents. find "$stateFile" -mtime +19 will return non-empty if the file is old enough.

Chris Davies
  • 116,213
  • 16
  • 160
  • 287