1. Overview

More often than not, as system administrators, we have to run scripts at regular time intervals to keep our system infrastructure clean and secure.

These repetitive tasks include system backup, health monitoring, and other maintenance jobs. In such a case, automation is the best and quickest way to achieve the purpose.

This tutorial will expand on different methods of scheduling jobs at a specific time in Linux systems.

2. Cron

cron is a generic software service for scheduling tasks. It comprises two key components: cron daemon (crond) and cron configuration. crond reads the cron configuration to determine when to run which task. It iterates all the files under /var/spool/cron, /etc/crontab, and /etc/cron.d to execute the commands.

Now, let’s get into the nitty-gritty of it.

2.1. crontab

crontab is the command-line utility to manage all cron jobs. The cron packages are available by default in all recent Linux variant systems. If not, we can install it using a package manager:

$ dpkg -l cron
dpkg-query: no packages found matching cron

$ sudo apt-get update && sudo apt-get upgrade -y
$ sudo apt-get install cron -y
...
...

$ dpkg -l cron
...
...

||/ Name                                 Version                 Architecture            Description
+++-====================================-=======================-=======================-=============================================================================
ii  cron                                 3.0pl1-128.1ubuntu1     amd64                   process scheduling daemon

$ systemctl status cron
● cron.service - Regular background program processing daemon
   Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: enabled)
   Active: active (running) since Sat 2021-08-14 20:51:25 IST; 1 weeks 2 days ago
...
...

The crontab -l command will list all cron jobs. We can add comments using the # symbol:

$ crontab -l
# Edit this file to introduce tasks to be run by cron.
...
...
#
0 5 * * 1 tar -zcf /var/backups/home.tgz /home/

As the vi editor is generally the preferred tool for editing the cron configuration, let’s change the default editor to vi through the export command and edit the cron table using crontab -e:

$ export EDITOR=vi
$ crontab -e
# Edit this file to introduce tasks to be run by cron.
0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
...
...
# [minute] [hours] [day of month] [month] [day of the week] command-to-execute

Each of these fields has an allowed range of integer values:

  • minute – 0 to 59
  • hour – 0 to 23
  • day of month – 1 to 31
  • month – 1 to 12
  • day of the week – 0 to 7, where 0 or 7 denotes Sunday, 1 is Monday, 2 is Tuesday, and so on, with 6 denoting Saturday

Let’s see a few examples of cron jobs. We’ll reroute the stdout and stderr to /dev/null to suppress the command output during execution:

To run backup.sh at 23:00 hrs (11 pm) daily and blackhole the stdout and stderr to the /dev/null file:

00 23 * * * /home/baeldung/backup.sh > /dev/null 2>&1

The system-wide cron environment variables can be set in /etc/crontab:

$ cat /etc/crontab
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
...

Furthermore, the system administrator has all privileges to restrict cron access through /etc/cron.allow and /etc/cron.deny files.

2.2. cron Special Characters

cron expressions typically contain one or more special characters that help us with scheduling tasks.

The asterisk (*) matches all possible values of a given field. Let’s see it in action:

00 15 * * * /home/baeldung/backups.sh

Here, by using an asterisk in the last three fields (1-31 days, 1-12 months, all days of the week), our backups.sh script will run every day at 3 pm.

The slash (/) denotes the increment of a given range:

02-50/10 * * * * /home/baeldung/monitor-cpu.sh

In the above expression, 2-50/10 in the minute field indicates the second minute of the hour and every 10 minutes thereafter. Therefore, the script will run on the 2nd, 12th, 22nd, 32nd, and 42nd minute of every hour.

The hyphen (-) signifies a continuous range, while the comma (,) separates a list of values.

The symbol at (@) enables the usage of an inbuilt cron scheduler such as @reboot, @hourly, @daily, @monthly, and @yearly. For example, we can schedule a script to execute after every reboot:

@reboot (cd /home/baeldung/monitoring-scripts; bash monitor-memory.sh)

Here, the semicolon (;) appends the commands to the execution list. In this example, first, it goes to the /home/baeldung/monitoring-scripts path and then executes the script monitor-memory.sh.

Our article on cron expressions further elucidates all possible options that cron uses.

Additionally, we can also place the scripts in /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, /etc/cron.monthly and /etc/cron.yearly directories for their respective execution:

$ ls -ltr /etc/cron.daily/
total 48
-rwxr-xr-x 1 root root  372 Aug 21  2017 logrotate
-rwxr-xr-x 1 root root 1176 Nov  3  2017 dpkg
-rwxr-xr-x 1 root root  249 Jan 25  2018 passed
...

The above scripts residing in the cron.daily directory execute every day.

Generally, we should avoid running the cron task scheduler for the root user. Likewise, it’s not recommended to have complex code, piping, or redirections directly on the cron command. Instead, we should define complex jobs as scripts and call the scripts from cron.

3. at

at is yet another scheduler service that allows us to execute commands at a specific time, but only once.

The at packages are available by default in all recent Linux variant systems. If not, we can install it using a package manager:

$ which at
$

$ sudo apt install at -y
...
...

$ which at
/usr/bin/at

We can easily schedule the tasks through the command line by giving the time parameter.

Let’s have a look now:

$ date
Wed Aug 25 06:49:57 IST 2021
$ date > current-time.txt | at now
warning: commands will be executed using /bin/sh
job 9 at Wed Aug 25 06:50:21 2021
$ cat current-time.txt
Wed Aug 25 06:50:22 IST 2021

The inline command execution helps us to run a script by leveraging the -f option of the at command:

$ at 09:00 -f /home/baeldung/one-time-env-setup.sh

Alternatively, we can schedule the jobs in the at terminal. For the sake of illustration, let’s create a new job that runs at 06:42 hrs to write the date and time into the at-demo.txt file:

$ at 06:42
warning: commands will be executed using /bin/sh
at> date > at-demo.txt
at> 
job 2 at Sat Aug 14 06:42:00 2021

$ ls -ltrh
total 4.0K
-rw-rw-r-- 1 tools tools 29 Aug 14 06:42 at-demo.txt

$ cat at-demo.txt
Sat Aug 14 06:42:00 IST 2021

We can use the atq command to list the user-scheduled jobs in the at queue.

Let’s read from the left side of the output as job id, date-time, queue letter, and the user. The queue letters can be from a to z and A to Z, where lower-valued letters have lower nice values and higher-valued letters have higher nice values:

$ atq
2       Sat Aug 14 06:42:00 2021 a tools

The atrm command removes the job from the task scheduler list:

$ atq
14      Fri Aug 27 06:42:00 2021 a tools
$ atrm 2
$ atq
$

4. batch

Interestingly, the batch command is an extended feature of the at command. It executes a command based on CPU load and not on time parameters.

When the system’s average CPU load is less than 1.5, then the system will execute the scheduled commands.

An alternate way of calling the batch command is at -b.

First, let’s see an example of the batch command with a CPU load of less than 1.5.

Usually, the top command helps to identify the current CPU load:

top - 17:20:50 up 11 days, 20:29,  2 users,  load average: 0.00, 0.00, 0.00
Tasks: 405 total,   1 running, 306 sleeping,   0 stopped,   0 zombie

$ batch
warning: commands will be executed using /bin/sh
at> date > batch-test.txt
at> 
job 17 at Thu Aug 26 17:21:00 2021

$ ls -ltrh
-rw-rw-r-- 1 tools tools    29 Aug 26 17:22 batch-test.txt

$ cat batch-test.txt
Thu Aug 26 17:22:12 IST 2021

In this case, batch didn’t execute the scheduled command as the CPU load was more than 1.5:

top - 17:24:28 up 11 days, 20:37,  2 users,  load average: 6.91, 3.47, 1.38
Tasks: 413 total,   8 running, 306 sleeping,   0 stopped,   0 zombie

$ date
Thu Aug 26 17:26:45 IST 2021

$ batch
warning: commands will be executed using /bin/sh
at> date > batch-load-test.txt
at> 
job 19 at Thu Aug 26 17:26:00 2021

$ date
Thu Aug 26 17:28:49 IST 2021

$ ls -ltrh batch-load-test.txt
ls: cannot access 'batch-load-test.txt': No such file or directory

$ atq
19      Thu Aug 26 17:26:00 2021 b tools

We can restrict the users accessing the at and batch services using /etc/at.deny and /etc/at.allow files.

Now, let’s block the user jack from accessing at and batch services by simply adding the username into the /etc/at.deny file:

root@sandbox1:~$ cat /etc/at.deny | grep jack
jack

jack@sandbox1:~$ at
You do not have permission to use at.

jack@sandbox1:~$ batch
You do not have permission to use at.

5. Conclusion

To summarize, we explored the different ways of scheduling tasks using cron, at, and batch services. Along the way, we saw the simplicity and flexibility of cron services in the repetitive execution of the jobs.

On the other hand, the at and batch services execute a task only once.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.