How to Automatically Run Shell Scripts on a Mac Using Cron Jobs

I wanted to run a shell script at regular intervals to update a greeting image on my Tidbyt device. The script had to render the image and push it to Tidbyt so that the greeting would refresh every few minutes. While developing this, I realized I needed the script to execute automatically from my Mac at specific intervals.

I had heard of cron jobs as a way to do this but hadn't dived into setting one up myself. After a bit of learning and tinkering, I figured it out. So, I thought I'd document the basics, not just for future me who might forget how all this works, but also for anyone else who finds cron jobs intimidating or complex.

What is a Cron Job and Where are its Rules Stored?

A cron job is a scheduled task that runs automatically at specified intervals on your Mac. When you set up a cron job, you're essentially instructing macOS's "cron" system to execute a particular script or command at a certain time.

This task scheduling is managed by a background service called the cron daemon.

The rules for these jobs aren't stored in a file you'd casually browse through in Finder; instead, they are kept in a special table called a "crontab." You interact with this table using the crontab command in the Terminal.

How Do I Add a Rule/Edit crontab?

You add a rule by opening the Terminal and typing cr

ontab -e. This opens an editor where you can specify your rules. Each rule you add tells macOS what command to run and when to run it. The "cron" system will read this table and execute the commands accordingly.

How to Add a Rule to Run a Script Every Minute:

  1. Open Terminal, and type crontab -e. This opens your crontab file in an editor.
  2. Add Your Rule: Type in * * * * * /Users/yourusername/my_script.sh (replace the path with the actual location where you've saved my_script.sh).
  3. Save and Close: In Vim, you'd press Esc, then type :wq and hit Enter. In Nano, it's Ctrl + O to save, then Ctrl + X to exit.

The cron daemon will now read this new entry and run my_script.sh every minute.

Understanding Cron Syntax

To schedule tasks using cron on macOS, the basic syntax is:

* * * * * /path/to/your/command_or_script.sh

Each of the five asterisks represents a unit of time, in this order:

  1. Minute (0 - 59)
  2. Hour (0 - 23)
  3. Day of the month (1 - 31)
  4. Month (1 - 12)
  5. Day of the week (0 - 7, where both 0 and 7 represent Sunday)

To make it easier to understand, we'll use the following format to explain the examples:

M H DM M DW <script>

Where:

  • M represents Minute
  • H represents Hour
  • DM represents Day of the Month
  • M represents Month
  • DW represents Day of the Week

This will make it easier to understand the structure of each cron job example.

M H DM M DW <script>

Asterisk means "every"

An asterisk (*) in a cron job field means "all" or "every" for that particular unit of time. For example, an asterisk in the hour field would mean "every hour," and an asterisk in the day-of-the-week field would mean "every day of the week."

Use Full Path

The /path/to/your/command_or_script.sh is the command or script you want to run at the specified time. When you add a rule, you specify the full path to the script you want to run. macOS uses this full path to find and run your script. For example, /Users/yourusername/my_script.sh is the full path to the script.

* * * * * /Users/yourusername/my_script.sh

Example 1: Run a Script at 9 a.m. Every Day

To run a script every day at 9 AM, your cron job would look like this:

M H DM M DW <script>
0 9 * * * /Users/yourusername/your_daily_script.sh

Here, the 0 represents the minute, and 9 represents the hour. The three asterisks after that signify "every day of the month," "every month," and "every day of the week," respectively.

Example 2: Run a Script at 12:30 p.m. Every Monday

To run a script every Monday at 12:30 PM, your cron job would look like this:

M  H DM M DW <script>
30 12 * * 1 /Users/yourusername/your_monday_script.sh

Here, the 30 represents the minute, and 12 represents the hour. The first two asterisks signify "every day of the month" and "every month." The 1 at the end represents Monday in the "day of the week" field. In cron syntax, the week starts with Sunday as 0 or 7, followed by Monday as 1, up to Saturday which is 6.

Example 3: Run a Script Every 15 Minutes

If you need a script to run every 15 minutes, your cron job would be:

M    H DM M DW <script>
*/15 * * * * /Users/yourusername/your_frequent_script.sh

The */15 tells the cron system to run the script every 15 minutes.

In both examples, replace /Users/yourusername/your_script.sh with the actual full path to your script.

Make sure your script has the appropriate permissions to run (chmod +x /path/to/your/script.sh).

Example 4: Run a Script Every 3 Hours

To execute a script every 3 hours, your cron job entry would look like this:

M  H DM M DW <script>
0 */3 * * * /Users/yourusername/your_three_hour_script.sh

In this case, the 0 signifies that the script will run at the beginning of the hour, and */3 indicates it will repeat every 3 hours.

Fin!