A terminal window on a Linux system.
Fatmawati Achmad Zaenuri/Shutterstock

If you want to schedule a Linux job that will happen only once, cron is overkill. The at family of commands is what you need! And if you want to run processes only when your system has free resources, you can use batch.

How to Schedule Linux Jobs

The cron daemon maintains a list of jobs it runs at specific times. These tasks and programs run in the background at the scheduled times. This offers you great flexibility for scheduling tasks that need to be repeated. Whether you need to run a task once every hour, at a specific time each day, or once a month or year, you can set it up in cron.

However, this doesn’t help if you want to schedule a task to run just once. Sure, you can set it up in cron, but then you have to remember to go back and remove the crontab entry after the task executes, which is inconvenient.

With Linux, if you’re struggling with a problem, it’s almost a guarantee someone else has struggled with it, too. Fortunately, because Unix-like operating systems have been around so long, there’s also an excellent chance someone has created a solution to your problem.

For the problem outlined above, they have, and it’s called at.

RELATED: How to Schedule Tasks on Linux: An Introduction to Crontab Files

Installing the at Command

We had to install at on Ubuntu 18.04 and Manjaro 18.1.0 (it was already installed on Fedora 31).

To install at on Ubuntu, use this command:

sudo apt-get install at

The "sudo apt-get install at" command in a terminal window.

After the installation completes, you can start the at daemon with this command:

sudo systemctl enable --now atd.service

The "sudo systemctl enable --now atd.service" command in a terminal window.

On Manjaro, you install at with this command:

sudo pacman -Sy at

The "sudo pacman -Sy at" command in a terminal window.

After the installation completes, type this command to start the at daemon:

sudo systemctl enable --now atd.service

The "sudo systemctl enable --now atd.service" command in a terminal window.

On any distribution, you can type this command to make sure the atd daemon is running:

ps -e | grep atd

The "ps -e | grep atd" command in a terminal window.

How to Use the at Command Interactively

To use at, you have to assign it a date and time to run. There’s a great deal of flexibility in the way you can write these, which we cover later in this article.

However, even though we’re going to use at interactively, you have to provide the date and time upfront. If you don’t include anything on the command line, or you type something that isn’t a date and time, at responds with “Garbled time,” as shown below:

at
at banana

An incorrect use of the "at" command in a terminal window.

Dates and times can be explicit or relative. For example, let’s say you want to have a command execute one minute from now. at knows what “now” means, so you can use now and add one minute to it, like so:

at now + 1 minute

The "at now + 1 minute" command in a terminal window.

at prints out a message and an at prompt, and waits for you to type the commands you want to schedule. First, though, consider the message, as shown below:

The "at" command prompt in a terminal window.

It tells you at launches an instance of the sh shell and will run the commands inside that. Your commands won’t be executed in the Bash shell, which is compatible with the sh shell but has a richer feature set.

If your commands or scripts try to use a function or facility that Bash provides, but sh doesn’t, they’ll fail.

It’s easy to test whether your commands or scripts will run in sh. Use the sh command to start an sh shell:

sh

The "sh" shell in a terminal window.

The command prompt changes to a dollar sign ($), and you can now run your commands and verify that they operate correctly.

To return to the Bash shell, type the exit command:

exit

You won’t see any standard output or error messages from the commands. This is because the sh shell launches as a background task and runs without any sort of screen interface.

Any output from the commands—good or bad—is emailed to you. It’s sent via the internal mail system to whomever runs the at command. This means you have to set up and configure that internal email system.

Many (most) Linux systems don’t have an internal email system as there’s rarely a need for one. Those that do typically use a system like sendmail or postfix. If your system doesn’t have an internal email system, you can have scripts write to files or redirect output to files to add logging.

If the command doesn’t generate any standard output or error messages, you won’t get an email, anyway. Many Linux commands indicate success via silence, so in most cases, you won’t get an email.

RELATED: What Are stdin, stdout, and stderr on Linux?

Now, it’s time to type a command in at. For this example, we’ll use a small script file called sweep.sh that deletes the *.bak, *.tmp, and *.o files. Type the path to the command, as shown below, and then press Enter.

The "~/sweep.sh" command in a terminal window.

Another command prompt appears, and you can add as many commands as you like. It’s usually more convenient to have your commands in a single script and simply call that script from within at.

Press Ctrl+D to tell at you’re finished adding commands. at shows <EOT>, which means end of transmission. You’re told the job number and when the job is scheduled to run, as shown below:

Confirmation of "job 4" scheduled to run "Wed Dec 18 08:37:00 2019" in a terminal window.

After the job executes, type the following to check your internal mail:

mail

The "mail" command in a terminal window.

If there’s no mail, you have to assume success. Of course, in this case, you can check and see if the *.bak , *.tmp, and *.o files were deleted to confirm the command worked.

Type the following to run the whole thing again:

at now + 1 minute

The "at now + 1 minute" command in a terminal window.

After one minute, type the following to recheck your mail:

mail

The "mail" command indicating "1 message 1 unread" in a terminal window.

Hey, we’ve got mail! To read message number one, press 1, and then hit Enter.

An internal email in a terminal window.

We received an email from at because the commands in the script generated error messages. In this example, there were no files to delete because when we ran the script previously, it removed them.

Press D+Enter to delete the email and Q+Enter to quit the mail program.

Date and Time Formats

You have a lot of flexibility when it comes to the time formats you can use with at. Here are a few examples:

  • Run at 11:00 a.m.:
    • at 11:00 AM
  • Run at 11:00 a.m. tomorrow:
    • at 11:00 AM tomorrow
  • Run at 11:00 a.m. on this day next week:
    • at 11:00 AM next week
  • Run at this time, on this day, next week:
    • at next week
  • Run at 11:00 a.m. next Friday:
    • at 11:00 AM next fri
  • Run at this time next Friday:
    • at next fri
  • Run at 11:00 a.m. on this date, next month:
    • at 11:00 AM next month
  • Run at 11:00 a.m. on a specific date:
    • at 11:00 AM 3/15/2020
  • Run 30 minutes from now:
    • at now + 30 minutes
  • Run two hours from now:
    • at now + 2 hours
  • Run at this time tomorrow:
    • at tomorrow
  • Run at this time on Thursday:
    • at thursday
  • Run at 12:00 a.m.:
    • at midnight
  • Run at 12:00 p.m.:
    • at noon
  • If you’re a Brit, you can even schedule a command to run at teatime (4 p.m.):
    • at teatime

Looking at the Job Queue

You can type the atq command to see the queue of scheduled jobs, as shown below.

Output from the "atq" command showing 5 jobs in a terminal window.

For each command in the queue, atq displays the following information:

  • Job ID
  • Scheduled date
  • Scheduled time
  • Queue the job is in. The queues are labeled “a,” “b,” and so on. Normal tasks you schedule with at go into queue “a,” while tasks you schedule with batch (covered later in this article) go into queue “b.”
  • The person who scheduled the job.

Using at on the Command Line

You don’t have to use at interactively; you can also use it on the command. This makes it easier to use inside scripts.

You can pipe commands into at, like this:

echo "sh ~/sweep.sh" | at 08:45 AM

An "echo "sh ~/sweep.sh" | at 08:45 AM" command in a terminal window.

The job is accepted and scheduled by at, and the job number and execution date are reported just as before.

RELATED: How to Use Pipes on Linux

Using at with Files of Commands

You can also store a sequence of commands in a file, and then pass it to at. This can be a plain text file of commands—it doesn’t have to be an executable script.

You can use the -f (file) option in the following way to pass a filename to at:

at now + 5 minutes -f clean.txt

An "at now + 5 minutes -f clean.txt" command in a terminal window.

You can achieve the same result if you redirect the file into at:

at now + 5 minutes < clean.txt

An "at now + 5 minutes < clean.txt" command in a terminal window.

Removing Scheduled Jobs from the Queue

To remove a scheduled job from the queue, you can use the atrm command. If you want to see the queue first to find the number of the job you want to remove, you can use atq . Then, use that job number with atrm, as shown below:

atq
atrm 11
atq

Removing job 11 from the at queue in a terminal window.

How to See a Detailed View of Jobs

As we mentioned previously, you can schedule jobs far into the future. Sometimes, you might forget what a job is going to do. The atq command shows you the jobs in the queue, but not what they’re going to do. If you want to see a detailed view of a job, you can use the -c (cat) option.

First, we’ll use atq to find the job number:

atq

Output from the "atq" command in a terminal window.

Now, we’ll use job number 13 with the -c option:

at -c 13

The "at -c 13" command and output in a terminal window.

Here’s a breakdown of the information we get back about the job:

  • First line: This tells us the commands will run under the sh shell.
  • Second line: We see the commands will run with both a user and group ID of 1000. These are the values for the person who ran the at command.
  • Third line: The person who receives any emails atsends.
  • Fourth line: The User Mask is 22. This is the mask used to set the default permissions for any files created in this sh session. The mask is subtracted from 666, which gives us 644 (the octal equivalent of rw-r--r--).
  • Remaining data: The majority are environment variables.

Output from the "-c 13" command in a terminal window.

  • Results of a test. A test checks to make sure the execution directory can be accessed. If it cannot, an error is raised, and the job execution is abandoned.
  • The commands to be executed. These are listed, and the contents of the scripts that are scheduled are displayed. Note that although the script in our example above was written to run under Bash, it will still be executed in an sh shell.

The batch Command

The batch command operates similarly to the at command, but with three significant differences:

  1. You can only use the batch command interactively.
  2. Rather than scheduling jobs to execute at a specific time, you add them to the queue, and the batch command executes them when the system’s average load is lower than 1.5.
  3. Due to the above, you never specify a date and time with the batch command.

When you use the batch command, you call it by name with no command line parameters like so:

batch

The "batch" command in a terminal window.

Next, add tasks just as you would with the at command.

Controlling Access to the at Command

The at.allow and at.deny files control who can use the at family of commands. These are located within the /etc directory. By default, only the at.deny file exists, and it’s created when at is installed.

Here’s how these work:

  • at.deny: Lists applications and entities that cannot use at to schedule jobs.
  • at.allow: Lists who can use at to schedule jobs. If the at.allow file doesn’t exist, at only uses the at.deny file.

By default, anyone can use at. If you want to restrict who can use it, use the at.allow file to list those who can. This is easier than adding everyone who cannot use at to the at.deny file.

Here’s what the at.deny file looks like:

sudo less /etc/at.deny

The "sudo less /etc/at.deny" command in a terminal window.

The file lists components of the operating system that cannot use at. Many of these are prevented from doing so for security reasons, so you don’t want to remove any from the file.

The contents of the at.deny file in a terminal window.

Now, we’ll edit the at.allow file. We’re going to add dave and mary, but no one else will be allowed to use at.

First, we type the following:

sudo gedit /etc/at.allow

The "sudo gedit /etc/at.allow" command in a terminal window.

In the editor, we add the two names, as shown below, and then save the file.

"dave" and "mary" added to gedit.

If anyone else tries to use at, he’ll be told he doesn’t have permission. For example, let’s say a user named eric types the following:

at

He would be refused, as shown below.

"eric" is refused permission to use "at" in a terminal window.

Again, eric is not in the at.deny file. As soon as you put anyone in the at.allow file, any- and everyone else is denied permission to use at.

Great for One-Offs

As you can see, both at and batch are ideal for tasks you only need to run once. Again, as a quick review:

  • When you need to do something that isn’t a regular process, schedule it with at.
  • If you want to run a task only when the system load is low enough, use batch.
Dave McKay Dave McKay
Dave McKay first used computers when punched paper tape was in vogue, and he has been programming ever since. He is now a Data Protection Officer and has worked as a freelance programmer, manager of an international software development team, and an IT services project manager. Dave is a Linux evangelist and open source advocate.
Read Full Bio »