How to schedule one-time task

Schedule one-time task at specific time or below defined system load using at utility.

Install required software

This utility should be available by default, but in case it isn't, use the following command to install it.

$ sudo apt-get install at

Define users that are allowed to submit jobs

Use /etc/at.allow or /etc/at.deny file to allow or deny users to execute this utility.

The root with uid = 0 is above these rules and can always execute at utlity.

If the /etc/at.allow file exists then it is used to define users that are allowed to execute at utlity.

If the /etc/at.allow does not exist, then /etc/at.deny file is used to define users that are not allowed to execute at utlity.

Look at the following application's source code if you have any doubts.

/* Global functions */
int
check_permission()
{
  uid_t uid = geteuid();
  struct passwd *pentry;
  int    allow = 0, deny = 1;

  if (uid == 0)
    return 1;

  if ((pentry = getpwuid(uid)) == NULL) {
    perror("Cannot access user database");
    exit(EXIT_FAILURE);
  }

  allow = user_in_file(ETCDIR "/at.allow", pentry->pw_name);
  if (allow==0 || allow==1)
    return allow;

  /* There was an error while looking for pw_name in at.allow.
   * Check at.deny only when at.allow doesn't exist.
   */

  deny = user_in_file(ETCDIR "/at.deny", pentry->pw_name);
  return deny == 0;
}

Schedule one-time jobs at specific time

Schedule commands to execute now.

$ at now
warning: commands will be executed using /bin/sh
at> some | commands
at> <EOT>
job 30 at Wed Nov 15  9:53:00 2017

Schedule commands to execute after two hours and do not send mail to user.

$ at -M now + 2 hours
warning: commands will be executed using /bin/sh
at> some | commands
at> <EOT>
job 26 at Wed Nov 15 11:54:00 2017

Schedule commands to execute at 14:00 on December 16, 2018 and send mail to user even if there was no output.

$ at -m 14:00 16.12.2018
warning: commands will be executed using /bin/sh
at> some | commands
at> <EOT>
job 27 at Sun Dec 16 14:00:00 2018

You can specify date using any from the following format: MMDD[CC]YY, MM/DD/[CC]YY, DD.MM.[CC]YY or [CC]YY-MM-DD.

Schedule /opt/bin/reminder.sh shell script to execute tomorrow at 13:30.

$ at -f /opt/bin/reminder.sh 13:30 tomorrow
warning: commands will be executed using /bin/sh job 29 at Thu Nov 16 13:30:00 2017

Use the -v option to display the time the job will be executed before defining it.

$ at -v now + 4 hours
Wed Nov 15 13:55:00 2017

warning: commands will be executed using /bin/sh
at> some | commands
at> <EOT>
job 30 at Wed Nov 15 13:55:00 2017

There are more possibilities as there are many time modifiers available.

now             { COPY_TOK ; return NOW; }
am              { COPY_TOK ; return AM; }
pm              { COPY_TOK ; return PM; }
noon            { COPY_TOK ; return NOON; }
midnight        { COPY_TOK ; return MIDNIGHT; }
teatime         { COPY_TOK ; return TEATIME; }
sun(day)?       { COPY_TOK ; return SUN; }
mon(day)?       { COPY_TOK ; return MON; }
tue(sday)?      { COPY_TOK ; return TUE; }
wed(nesday)?    { COPY_TOK ; return WED; }
thu(rsday)?     { COPY_TOK ; return THU; }
fri(day)?       { COPY_TOK ; return FRI; }
sat(urday)?     { COPY_TOK ; return SAT; }
today           { COPY_TOK ; return TODAY; }
tomorrow        { COPY_TOK ; return TOMORROW; }
next            { COPY_TOK ; return NEXT; }
min             { COPY_TOK ; return MINUTE; }
minute(s)?      { COPY_TOK ; return MINUTE; }
hour(s)?        { COPY_TOK ; return HOUR; }
day(s)?         { COPY_TOK ; return DAY; }
week(s)?        { COPY_TOK ; return WEEK; }
month(s)?       { COPY_TOK ; return MONTH; }
year(s)?        { COPY_TOK ; return YEAR; }
jan(uary)?      { COPY_TOK ; return JAN; }
feb(ruary)?     { COPY_TOK ; return FEB; }
mar(ch)?        { COPY_TOK ; return MAR; }
apr(il)?        { COPY_TOK ; return APR; }
may             { COPY_TOK ; return MAY; }
jun(e)?         { COPY_TOK ; return JUN; }
jul(y)?         { COPY_TOK ; return JUL; }
aug(ust)?       { COPY_TOK ; return AUG; }
sep(tember)?    { COPY_TOK ; return SEP; }
oct(ober)?      { COPY_TOK ; return OCT; }
nov(ember)?     { COPY_TOK ; return NOV; }
dec(ember)?     { COPY_TOK ; return DEC; }
utc             { COPY_TOK ; return UTC; }
[0-9]{1}        { COPY_TOK ; COPY_VAL; return INT1DIGIT; }
[0-9]{2}        { COPY_TOK ; COPY_VAL; return INT2DIGIT; }
[0-9]{4}        { COPY_TOK ; COPY_VAL; return INT4DIGIT; }
[0-9]{5,8}      { COPY_TOK ; COPY_VAL; return INT5_8DIGIT; }
[0-9]+          { COPY_TOK ; COPY_VAL; return INT; }
[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{2}([0-9]{2})?     { COPY_TOK ; COPY_VAL; return DOTTEDDATE; }
[0-9]{2}([0-9]{2})?-[0-9]{1,2}-[0-9]{1,2}       { COPY_TOK ; COPY_VAL; return HYPHENDATE; }
[012]?[0-9][:'h,.][0-9]{2}      { COPY_TOK ; COPY_VAL; return HOURMIN; }

You can read /usr/share/doc/at/timespec for more information.

Schedule one-time task when system goes below specific load

Schedule commands to execute when the load average drops below 1.5.

$ at -b
at> some | commands
at> <EOT>
job 31 at Wed Nov 15 09:55:56 2017

You can alter default value of load average that is used to determine when system load level is low enough to execute commands. To achieve this you need to modifiy daemon's startup parameters.

Queues

By default jobs are assigned to a queue and batch jobs to b queue.

You can assign custom queues from a to z and from A to Z. Jobs in an uppercase queues are treated as batch jobs at the specified time.

$ at -q F teatime
warning: commands will be executed using /bin/sh
at> some | commands
at> <EOT>
job 32 at Wed Nov 15 16:00:00 2017

Get job details

List scheduled jobs.

$ at -l
29	Thu Nov 16 13:30:00 2017 a root
30	Wed Nov 15 13:55:00 2017 a root
26	Wed Nov 15 11:54:00 2017 a root
27	Sun Dec 16 14:00:00 2018 a root
32	Wed Nov 15 16:00:00 2017 F root

Get details for specific job.

$ at -c 30
#!/bin/sh
# atrun uid=0 gid=0
# mail root 0
umask 22
LANG=C.UTF-8; export LANG
container=lxc; export container
USER=root; export USER
PWD=/root; export PWD
HOME=/root; export HOME
SHLVL=1; export SHLVL
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; export PATH
cd /root || {
	 echo 'Execution directory inaccessible' >&2
	 exit 1
}
some | commands

Remove jobs

Remove scheduled job.

$ at -r 9

System service

Service is configured using systemd service file.

$ cat /etc/systemd/system/multi-user.target.wants/atd.service
[Unit]
Description=Deferred execution scheduler
Documentation=man:atd(8)

[Service]
ExecStart=/usr/sbin/atd -f
IgnoreSIGPIPE=false

[Install]
WantedBy=multi-user.target

Use -l option to specify load average treshold that was mentioned earlier.

ExecStart=/usr/sbin/atd -f -l 4

Reload systemd manager configuration.

$ sudo systemctl daemon-reload

Restart service to apply changes.

$ sudo systemctl restart atd
Milosz Galazka's Picture

About Milosz Galazka

Milosz is a Linux Foundation Certified Engineer working for a successful Polish company as a system administrator and a long time supporter of Free Software Foundation and Debian operating system.