Skip to content
shellhacks.dev
Automation

Run a Cron Job on the Last Day of the Month

Cron has no last-day-of-month field. Run on days 28 to 31 and test in the command that tomorrow is the first, escaping the percent sign in date.

2 min read
Summarize in AI

A report must run on the last day of every month, and months have 28, 29, 30 or 31 days.

Shell
for d in 2026-02-27 2026-02-28 2026-03-30 2026-03-31 2028-02-28 2028-02-29; do
  if [ "$(date -d "$d + 1 day" +%d)" = 01 ]; then echo "$d: last day, run"; else echo "$d: skip"; fi
done
echo ---
echo '59 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /usr/local/bin/month-end.sh' | crontab -
crontab -l
crontab -r
Text
2026-02-27: skip
2026-02-28: last day, run
2026-03-30: skip
2026-03-31: last day, run
2028-02-28: skip
2028-02-29: last day, run
---
59 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /usr/local/bin/month-end.sh

Standard cron cannot say "the last day", so the job is scheduled for days 28 to 31 and a test at the start of the command lets it through only when tomorrow's day of the month is 01. The loop checks the logic against month ends, including February in a leap year. In a crontab the percent sign ends the command, so it must be escaped with a backslash, as crontab(5) says, which is what the second block does.

date -d is GNU; BSD and macOS date use -v+1d instead. Cron runs the line with /bin/sh, so the test uses only [ ] and no Bash features. When the day-of-month and day-of-week fields are both restricted, cron runs the job when either one matches, which is why the weekday field is left as a star. Some cron implementations support L in the day field, and Vixie cron does not.

Taggedcrondates
Share

More snippets

Automation

Wait for a File to Appear in a Bash Script

Wait for a file with an until loop on test -e, and have the writer create it under a temporary name and rename it, so the reader never sees half of it.

2 min read
Automation

Wait for a TCP Port to Open in a Bash Script

Wait until a TCP port accepts connections with a Bash loop that opens /dev/tcp/host/port, with no nc needed. It returns 0 once open, or 1 after the tries run out.

1 min read

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.