at
和cron
可以用于在指定的时间或日期运行命令和脚本,二者的主要区别是 at
命令用于仅运行一次的任务,而cron
用于定期地运行作业,cron
的频率通常设置为每天、每周或每月,也可以每年,由用户控制运行的频率和时间。二者都需要有 适当的权限是执行任务。二者一般共存。
安装和参考手册
1
2
3
$ sudo apt install at cron
$ man 1 at
$ man 8 cron
at
at程序安装完后,会有一个守护进程atd,应该是由它执行定时任务的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ sudo apt install at
$ sudo systemctl status atd
● atd.service - Deferred execution scheduler
Loaded: loaded (/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2023-02-14 17:29:04 CST; 7min ago
Docs: man:atd(8)
Process: 1288 ExecStartPre=/usr/bin/find /var/spool/cron/atjobs -type f -name =* -not -newercc /run/systemd -delete (code=exited, status=0/>
Main PID: 1330 (atd)
Tasks: 1 (limit: 308970)
Memory: 644.0K
CGroup: /system.slice/atd.service
└─1330 /usr/sbin/atd -f
2月 14 17:29:04 debian systemd[1]: Starting Deferred execution scheduler...
2月 14 17:29:04 debian systemd[1]: Started Deferred execution scheduler.
需要注意的:
(1)因为平时程序的运行都是在/bin/bash 这种shell中执行的,这个shell里面是有很多环境变量的,命令行敲的 程序很可能使用了部分shell的环境变量。而在at
中,是没有设置环境变量的,如果用到了环境环境(尤其是脚本程序),需要先设置, 可以在脚本中设置好,然后调用可执行程序。
(2)bash一般都是有tty的,后台的atd则没有,所以标准输出,标准错误估计会丢失,需要使用syslog记录到日志中。 或者重定向到日志文件中。
at
指定的时间表达式
at
因为是在指定时间timespec,执行设置的程序或脚本,这个时间的格式是 at
规定的。 可以参考man 1 at
或 /usr/share/doc/at/timespec
,好像只能到分,不能精确到秒?
(1)HH:MM
格式,指定小时和分钟,24小时制。如10:01
,14:02
,19:25
这种。另外,也可以使用12小时制的,后面带上 AM/PM 即可。如 02:02 PM
就等效于 14:02
。另外还有几个特定的alias,midnight,noon,teatime,分别代表 00:00,12:00,16:00
(2)now + count time-units
格式,相对当前时间,时间单位有 minutes, hours, days, weeks ,如 now +1 hours
即1小时后, now +3 days
即3天后。now
是相对现在,具体还有其他,一般now
比较常见,具体的格式也较多,如 at 4pm + 3 days
, at 1am tomorrow
这种都是可以的,都是设置的方式,看的明白即可。详细的时间格式在文档中,了解常用方式即可。
(3)MMDD[CC]YY, MM/DD/[CC]YY, DD.MM.[CC]YY, [CC]YY-MM-DD
,The specification of a date must follow the specification of the time of day. 可以在时间后面加上日期进行补充,日期是英文中的表达方式,月日年 MMDD[CC]YY, MM/DD/[CC]YY, DD.MM.[CC]YY,而年月日只有 [CC]YY-MM-DD, [CC]YY
表示完整年,YY
是简写年,如2022年,CC就是20,YY就是22。如果指定的时间已经过时了,则at
会立即执行。
(4)其他格式,比如还可以使用月份 at 10am Jul 31
等。
(5)使用 -t
参数,-t time
run the job at time, given in the format [[CC]YY]MMDDhhmm[.ss],测试,秒不起作用
前台交互式at
命令
at 后跟一个timespec,然后输入命令,最后输入EOF结束。 如果时间格式不对,at会直接报错,Garbled time
。
1
2
3
4
5
6
$ at 10:45 02152023
warning: commands will be executed using /bin/sh
at> echo "my test date and time" >> ~/qwer.txt
at> date >> ~/qwer.txt
at> <EOT>
job 10 at Wed Feb 15 10:45:00 2023
也可以使用管道
1
$ echo "date >> ~/at-test.txt" | at now +2 minute
使用 -t 参数设置的时间格式
1
2
3
4
5
$ at -t 202302151103.12
warning: commands will be executed using /bin/sh
at> date >> ~/qwer.txt
at> <EOT>
job 12 at Wed Feb 15 11:03:00 2023
at配置的命令最终是写在文件中的,如/var/spool/cron/atjobs,所以重启后仍存在
查看at
作业
使用 at -l
或 atq
joy为当前用户名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ which atq | xargs file
/usr/bin/atq: symbolic link to at
$ at -l
6 Wed Feb 15 13:00:00 2023 a joy
4 Wed Feb 15 18:01:00 2023 a joy
5 Wed Feb 15 13:02:00 2023 a joy
9 Sat Feb 18 16:00:00 2023 a joy
$ atq
6 Wed Feb 15 13:00:00 2023 a joy
4 Wed Feb 15 18:01:00 2023 a joy
5 Wed Feb 15 13:02:00 2023 a joy
9 Sat Feb 18 16:00:00 2023 a joy
第一列是job号。
查看具体job的执行命令
使用at -l
后列出了所有的jobs,查看具体某个job使用 -c
参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
joy@debian:~$ at -c 9
#!/bin/sh
# atrun uid=1001 gid=1001
# mail joy 0
umask 2
WK=/data/joy_workdir; export WK
LC_ADDRESS=zh_CN.UTF-8; export LC_ADDRESS
LC_NAME=zh_CN.UTF-8; export LC_NAME
LC_MONETARY=zh_CN.UTF-8; export LC_MONETARY
PWD=/home/joy; export PWD
LOGNAME=joy; export LOGNAME
XDG_SESSION_TYPE=tty; export XDG_SESSION_TYPE
MOTD_SHOWN=pam; export MOTD_SHOWN
HOME=/home/joy; export HOME
LC_PAPER=zh_CN.UTF-8; export LC_PAPER
LANG=en_US.UTF-8; export LANG
...省略一些环境变量
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin; export PATH
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1001/bus; export DBUS_SESSION_BUS_ADDRESS
SSH_TTY=/dev/pts/6; export SSH_TTY
LC_NUMERIC=zh_CN.UTF-8; export LC_NUMERIC
cd /home/joy || {
echo 'Execution directory inaccessible' >&2
exit 1
}
echo "test" >> /home/joy/qwer.txt
可以看出at是会为命令先设置一些环境变量的,可以检查一下。
删除job
使用 at -r
或 at -d
或 atrm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
joy@debian:~$ at -l
6 Wed Feb 15 13:00:00 2023 a joy
4 Wed Feb 15 18:01:00 2023 a joy
5 Wed Feb 15 13:02:00 2023 a joy
9 Sat Feb 18 16:00:00 2023 a joy
joy@debian:~$ which atrm | xargs file
/usr/bin/atrm: symbolic link to at
joy@debian:~$ at -r 5
joy@debian:~$ at -l
6 Wed Feb 15 13:00:00 2023 a joy
4 Wed Feb 15 18:01:00 2023 a joy
9 Sat Feb 18 16:00:00 2023 a joy
at
用户权限
at.allow, at.deny - determine who can submit jobs via at or batch
The /etc/at.allow
and /etc/at.deny
files determine which user can submit commands for later execution via at(1) or batch(1). The format of the files is a list of usernames, one on each line. Whitespace is not permitted. If the file /etc/at.allow
exists, only usernames mentioned in it are allowed to use at. If /etc/at.allow
does not exist, /etc/at.deny
is checked, every username not mentioned in it is then allowed to use at. An empty /etc/at.deny
means that every user may use at. If neither exists, only the superuser is allowed to use at.
是对哪些用户可以使用at的一个限制,不过root用户会忽略掉这里的限制。
at
小结
at
常用命令参数
opt | function |
---|---|
-m | Send mail to the user when the job has completed even if there was no output. |
-M | Never send mail to the user. |
-f file | Reads the job from file rather than standard input. |
-t time | run the job at time, given in the format [[CC]YY]MMDDhhmm[.ss] |
-l | Is an alias for atq. |
-r | Is an alias for atrm. |
-d | Is an alias for atrm. |
-c | cats the jobs listed on the command line to standard output. |
at
相比 cron
,特点是只运行一次,使用更简单一些。
batch命令
at命令还有可附带的命令batch,该命令功能和at基本一样, 唯一的区别是使用该命令设置的任务只会在系统空闲时执行。另外使用该命令设置的任务默认队列名称为b 。
cron
cron 可以用来循环执行定时任务。还有一个辅助扩展工具 anacron
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
joy@debian:~$ sudo apt install cron anacron
joy@debian:~$ sudo systemctl status cron.service
● cron.service - Regular background program processing daemon
Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2023-02-14 17:51:12 CST; 17h ago
Docs: man:cron(8)
Main PID: 1291 (cron)
Tasks: 1 (limit: 308970)
Memory: 484.0K
CGroup: /system.slice/cron.service
└─1291 /usr/sbin/cron -f
2月 15 10:17:01 debian CRON[2045733]: pam_unix(cron:session): session closed for user root
2月 15 10:30:01 debian CRON[2047061]: pam_unix(cron:session): session opened for user root by (uid=0)
2月 15 10:30:01 debian CRON[2047062]: (root) CMD ([ -x /etc/init.d/anacron ] && if [ ! -d /run/systemd/system ]; then /usr/sbin/invoke-rc.d anacro>
2月 15 10:30:01 debian CRON[2047061]: pam_unix(cron:session): session closed for user root
2月 15 11:17:01 debian CRON[2051858]: pam_unix(cron:session): session opened for user root by (uid=0)
2月 15 11:17:01 debian CRON[2051859]: (root) CMD ( cd / && run-parts --report /etc/cron.hourly)
2月 15 11:17:01 debian CRON[2051858]: pam_unix(cron:session): session closed for user root
2月 15 11:30:01 debian CRON[2053143]: pam_unix(cron:session): session opened for user root by (uid=0)
2月 15 11:30:01 debian CRON[2053144]: (root) CMD ([ -x /etc/init.d/anacron ] && if [ ! -d /run/systemd/system ]; then /usr/sbin/invoke-rc.d anacro>
2月 15 11:30:01 debian CRON[2053143]: pam_unix(cron:session): session closed for user root
crontab
配置工具
cron
是依据配置文件的,那些周期任务被写到了文件中,配置工具就是 crontab
工具,使用它来进行配置,而不是直接修改配置文件。 在Debian系统上,cron的相关配置文件,(CentOs的可能不同)
1
2
3
4
$ ls /etc/cron
cron.d/ cron.daily/ cron.hourly/ cron.monthly/ crontab cron.weekly/
# 以及 /var/spool/cron/crontabs
crontab
是cron
的真正配置工具,可以参考手册 man 1 crontab
。每个用户配置的crontab是独立的, /etc/cron.allow
和 /etc/cron.deny
类似at
工具的,可以配置用户是否允许使用cron。root
用户则无视这两个权限文件。
crontab
配置表达式
我们用crontab -e
进入当前用户的工作表编辑,可以选择nano 或 vim 编辑器,每一行是一条命令。具体配置格式参考 man 5 crontab
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ crontab -e
# (nano 编辑器中)
# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
每行命令有6个域,构成为 循环时间+动作,前面的5个用来设置循环周期,最后一个域设置执行的命令。 另外,可以在前面先填写一些环境变量,不过应该会被所有命令共享,不如写在命令脚本中。
时间域
1
<minute> <hour> <day-of-month> <month> <day-of-week> <command>
域 | 范围 | 描述 |
---|---|---|
min | 0-59 | 表示每个小时中的第xx分钟都要执行后面的命令 |
hour | 0-23 | 表示每天的第xx小时要执行命令 |
day-of=month | 1-31 | 表示每月的第xx天要执行命令 |
month | 1-12 | 表示每年的第xx月要执行命令 |
day-of-week | 0-7 | 表示每周的第xx天要执行命令,0和7都代表sunday |
如果全部设置,那么只能每年执行一次了,所以在范文中也有一些特殊的操作符可以填写。
特殊字符 | 功能 |
---|---|
* | 表示匹配该域的任意值。假如在hour域使用*, 即表示每小时都要执行命令,这个最常用 |
, | 表示枚举值,类似数组中的逗号用法 ,如在min域中写 “15,30,45”,就表示在每15和30和45分钟执行命令 |
- | 表示范围,如在day-of-month域写 “1-15”,表示每个月的第1天到第15天都执行 |
/ | 表示从起始时间A开始,每间隔B时间执行一次,如在min域写 “3/20”,表示第3分钟执行一次,后面23,43分钟也执行,一般前面的起始时间会写一个 * 号,如 “*/20” 这样用,就表示每20分钟执行一次 |
其他 | 还有一些其他的特殊用法,如 ?,L,W,# 等,不常用 |
有一些alias可以替代前面的5个时间域: | string | meaning | | —— | —————————— | | @reboot | Run once, at startup. | | @yearly | Run once a year, “0 0 1 1 *”. | | @annually | (same as @yearly) | | @monthly | Run once a month, “0 0 1 * *”. | | @weekly | Run once a week, “0 0 * * 0”. | | @daily | Run once a day, “0 0 * * *”. | | @midnight | (same as @daily) | | @hourly | Run once an hour, “0 * * * *”. |
命令域
命令域中最好写一个脚本,可以设置一些自己需要的环境变量。
example
每2分钟记录一次时间,使用 crontab -e
进入编写
1
*/2 * * * * /usr/bin/date >> /home/joy/test.txt
编写完后不需要额外执行其他动作,即可生效,
1
2
3
4
5
6
7
# wait some time
$ cat ~/test.txt
2023年 02月 15日 星期三 15:14:01 CST
2023年 02月 15日 星期三 15:16:01 CST
2023年 02月 15日 星期三 15:18:01 CST
2023年 02月 15日 星期三 15:20:01 CST
2023年 02月 15日 星期三 15:22:01 CST
删除周期任务
直接crontab -e
,删除相关行即可。 清空当前用户的 crontab ,可以执行 crontab -r
cron 小结
cron的任务管理是通过直接操作文本的,通过crontab -e
进入编辑即可,编辑完自动生效,环境变量用户自己管理。
其他的几个主要选项。
opt | meaning |
---|---|
-e | 编辑某个用户的 crontab 文件内容。如果不指定用户,则表示编辑当前用户的 crontab 文件。 |
-l | 显示某用户的 crontab 文件内容,如果不指定用户,则表示显示当前用户的 crontab 文件内容。 |
-r | 从 /var/spool/cron 删除某用户的 crontab 文件,如果不指定用户,则默认删除当前用户的 crontab 文件。 |
anacron
anacron
工具是一个cron工具的辅助工具,有一些扩展功能, 如cron 如果关机了,导致错过执行了一些命令,下次就不会执行了,配置anacron则可以让其执行; anacron还可以立即执行相关的任务命令,而cron不行。
二者的关系:anacron 与 cron 协同工作,因此严格来说前者不是后者的替代品,而是一种调度任务的有效可选方案。 许多系统管理员配置了一个 cron 作业来在深夜备份远程工作者计算机上的数据,结果却发现该作业在过去六个月中只运行过一次。 anacron 确保重要的工作在 可执行的时候 发生,而不是必须在安排好的 特定时间点 发生。
anacron使用自己的配置文件帮助cron实现扩展。
1
2
3
4
5
6
7
8
/etc/anacrontab
Contains specifications of jobs. See anacrontab(5) for a complete description.
/var/spool/anacron
This directory is used by Anacron for storing timestamp files.
/lib/systemd/system/anacron.service
This file provides systemd service for anacron.
/lib/systemd/system/anacron.timer
This file provides systemd timer for anacron. Currently the service is triggered hourly through systemd timer.
anacron 会以 1 天、1周(7天)、一个月作为检测周期,判断是否有定时任务在关机之后没有执行。如果有这样的任务,那么 anacron 会在特定的时间重新执行这些定时任务。 anacron 读取的时间记录文件。anacron 会分析现在的时间与时间记录文件所记载的上次执行 anacron 的时间,将两者进行比较, 如果两个时间的差值超过 anacron 的指定时间差值(一般是 1 天、7 天和一个月),就说明有定时任务没有执行, 这时 anacron 会介入并执行这个漏掉的定时任务,从而保证在关机时没有执行的定时任务不会被漏掉。
anacron 的配置
anacron 依赖 /etc/anacrontab
中的配置去检测cron是否有漏掉执行的任务。 该文件一般有root用户编辑,具体的格式参考 man 5 anacrontab
。
anacron安装后,有一个默认的 /etc/anacrontab
,里面有一些配置,符合日常使用场景需要了,不深入研究了。