Command-line Basics: Scheduling Tasks


Every programming language out there has one. A library written for the sole purpose of being able to run tasks on a schedule. That’s great and all, but in a lot of scenarios, it’s really unnecessary. Unix-like operating systems can do this (usually) out of the box, and they actually do it quite well.

Getting Started

For the sake of this post, I’m going to assume that you have the following commands already available to you as they are fairly standard issue with most Linux distributions. If they aren’t available, feel free to install them via your package manager of choice.

If you’re into docker, you can always spin up a container and play around in there:

$ docker run -it debian:latest bash

Once the container is up and running and you’re greeted with a prompt, you can install the commands used in this article:

$ EDITOR=vim # we need an editor, may as well be a good one ;)
$ apt update
$ apt install -y at cron vim
$ atd # to start the `at` daemon


cron, initially released in May 1975 (44 years ago at the time of this writing), is a tried and true time-based job scheduler. The name was inspired by the Greek word for time, which is chronos in English.

Before you try editing the crontab, let’s take a look at what’s already in there:

$ crontab -l

This will display the currently installed crontab. On a fresh system, the file should be loaded with a ton of information of how to configured a task inside of the crontab.

To edit the current user’s crontab, you can issue the following command:

$ crontab -e

Which will bring up the file for the current user in your editor of choice (for me, it’s vim, always).

This should bring up the same thing we just saw in our editor. The format of the entries in the crontab file are, from left to right:

  • Minute
  • Hour
  • Day of the month
  • Month
  • Day of the week
  • The command you’d like to run

For any of the “time” entries above, you can simply specify a wild card character, * to tell cron to “execute this every…”.

The most you can run a cron job is every minute, which is represented by:

* * * * * /some/awesome/command

Every minute of every day can be excessive in a lot of ways, so here’s a few, less aggressive examples:

*/15 * * * * # Every 15 minutes
   0 * * * * # Hourly at the top of the hour
  15 6 * * * # Daily at 6:15 AM
  44 4 * * 5 # Every Friday at 4:44 AM
   0 0 1 * * # Midnight on the first of the month
   0 0 1 1 * # Midnight on the first of the year

Once everything is how you’d like it, simply exit out of your editor (<ESC>:wq for vim) and the new tasks will be installed and run at the defined schedule.

Made a mistake and don’t want to save the new crontab? Simply exit your editor without saving the file, and cron will detect that no modifications were made, and won’t replace the currently installed crontab. If you’re using vim, that just means omitting the w and simply hitting <ESC>:q.

If remembering the format of the time values for cron isn’t at the top of your TODO list, there are some great resources out there to help. One of my favorite’s is crontab guru which allows you to define times to see what they translate to, but also provides a nice list of standard time strings that you can easily copy and paste.

If you have a script that has to be run as root, you can repeat the aforementioned command by using sudo or su -c depending on which you have available:

$ sudo crontab -e    # If you have sudo
$ su -c 'crontab -e' # If you don't

There’s nothing wrong with managing your crontab this way, but speaking from experience, it’s quite easy to slip -r into the command and lose your currently installed crontab completely.

You could very well alias crontab to crontab -i to ensure you’re prompted before a catastrophic removal, or you could start loading your crontab from a file:

$ crontab /path/to/crontab

The advantage here is that you could very well include the crontab in a project’s repository. The file will then be under version control with distributed backups by design.

If you wanted to take the current crontab and save it to a file, you can use the -l flag from earlier to send the contents to a file:

$ crontab -l > /path/to/crontab


The cron utility is great for scheduling repeating tasks, down to the minute, but isn’t necessarily suited for those times that we simply want to run a task later. For one-off tasks, you can use the at command to schedule a one time only tasks:

$ at now + 2 minutes

Will greet you with the at> prompt which you can enter one or more commands into:

$ at> echo "Hello World!" > /tmp/output.txt

Once you’ve entered in the command or commands you’d like to run, simply press CTRL+D to exit. Upon exiting, at will let you know the job number and what time the task will execute.

After the time has passed, you can use cat to verify that our at command ran:

$ cat /tmp/output.txt # Hello World!

at also has a couple of other commands that you can use, atq to show the pending jobs you have for the current user and atrm to remove a job by it’s ID.

Both of these commands are also conveniently aliased to arguments that can be passed to at directly. Let’s say we scheduled a command for next month:

$ at 12:34 PM next month

At the at> prompt, enter in the following, or the command of your choosing:

$ $ at> echo "This is next month's problem"

And hit CTRL-D to save and exit the at> prompt. Now that our task has been added to the queue, we can use the following commands to interact with it:

# List the scheduled tasks
$ at -l

# Show the task to be run by ID
$ at -c 1

# Removes the task by ID
$ at -r 1

# ALSO removes the task by ID
$ at -d 1

Unfortunately, there’s not a way to edit a task once it’s been added to the at schedule so in that scenario, you will need to delete a task and then re-add it.

A few other notable arguments that at can accept are:

# Load from a file, skips the at> prompt
$ at tomorrow -f /path/to/script

# Send mail to the current user
$ at tomorrow -m

The at command is even available on Microsoft Windows machines via the at.exe command. The arguments are a bit more “Windowsy” so YMMV.


The command-line is pretty powerful stuff and a lot of the utilities out there have been battle tested for decades. While older isn’t always better, utilizing these tried and true technologies allows you the freedom to switch between languages easier since you’re not tightly coupled to specific packages.

The caveat with leveraging cron instead of a language specific library is that you are limited to running every minute at it’s fastest. Language specific schedulers are implemented more as a daemon or long-running task which allows them the flexibility of running every second (and faster) if need be.

  Tweet It

🕵 Search Results

🔎 Searching...