Linux 中的定时任务可以分为 at 和 cron 两种。
at
在某个时间仅执行一次
crontab
从名字上看很像我们常用的 cron 表达式,该命令可以指定时间进行周期性执行任务
at 命令需要启动 atd 服务。
安装
yum -y install at
开机启动和状态查看:
[root@localhost ~]# systemctl enable atd
[root@localhost ~]# systemctl status atd
● atd.service - Job spooling tools
Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
Active: active (running) since 四 2021-09-09 20:57:55 CST; 14s ago
Main PID: 14283 (atd)
CGroup: /system.slice/atd.service
└─14283 /usr/sbin/atd -f
9月 09 20:57:55 localhost.localdomain systemd[1]: Started Job spooling tools.
Hint: Some lines were ellipsized, use -l to show in full.
at 命令产生的任务都将以文本文件的方式写入/var/spoll/at
目录内,等待 atd 服务的执行。我们创建了 at 任务后,就交给 atd 服务了,其会在后台运行我们的任务。
用户使用 at 的权限受/etc/at.allow
和/etc/at.deny
两个文件限制。
at.allow
文件存在,则只有文件内的用户可以使用 at 命令,其他用户都不可使用;at.deny
文件不存在,则只有at.deny
文件中的用户不可使用 at命令,其他用户都可以使用【注意,仅在 at.allow 不存在时, 该文件才生效】语法结合下面的2.3
示例1:
在et.deny
中添加用户 xiaoyunshi,然后切换到 xiaoyunshi 用户执行任务,发现没有权限执行:
[root@localhost ~]$ cat /etc/at.deny
xiaoyunshi
[xiaoyunshi@localhost ~]$ at now +1 minutes
You do not have permission to use at.
接着删除 at.deny 文件的 xiaoyunshi,就可以执行了:
[xiaoyunshi@localhost ~]$ at now +1 minutes
at> ls ~
at> <EOT>
job 8 at Thu Sep 9 21:36:00 2021
示例2:
在at.allow
文件中添加test
用户,表明只有 test 可以执行 at 命令【当然 root 也可以】:
# xiaoyunshi 用户又无法执行了
[xiaoyunshi@localhost ~]$ at now +1 minutes
You do not have permission to use at.
[xiaoyunshi@localhost ~]$ exit
登出
# 切换 test 用户
[root@localhost ~]# su - test
# 可以执行
[test@localhost ~]$ at now + 1minutes
at> ls ~
at> <EOT>
job 10 at Thu Sep 9 21:39:00 2021
at [参数] 时间
-m
at 执行完毕后,无论有没有标准输出,都会发送邮件给执行者【看下面的例1】
-l
【相当于 atq】
列出当前系统当前用户的所有 at 任务
[root@localhost mail]# at -l
2 Fri Oct 1 10:00:00 2021 a root
-d + 任务 id
【相当于 atrw】
取消一个 at 任务
[root@localhost mail]# atrm 2
[root@localhost mail]# atq
-v
使用明显的时间格式列出 at 中的任务列表【我没懂】
-c + 任务 id
输出当前任务实际执行的命令内容 【不重要】
时间格式
HH:MM
HH:MM YYYY-MM-DD
HH:MM[ am | pm ] [ Month ] [ Date ]
如: 11am Obtober 1
10月1日上午11点执行。14:23 July 2
:7月2日下午2点23执行
HH:MM[ am | pm ] + num [minutes | hours | days | weeks ]
某个时间点后加一断时间后执行,如:03pm + 3 weeks
当天下午的3点后加3周后执行
可以看到执行的最小单位是分钟,而不像我们其他程序中,可以精确到秒
1:执行一个1分钟后的任务,列出/root/at_dir
目录文件列表,如下,此时没有任何文件:
[root@localhost at_dir]# pwd
/root/at_dir
[root@localhost at_dir]# ll
总用量 0
执行一个 at 任务:
[root@localhost at_dir]# at now + 1 minutes
at> ls /root/at_dir
at> <EOT>
job 3 at Thu Sep 9 21:20:00 2021
在输入完 at 命令后,会进入 at>
命令行,此时就可以输入要执行的任务了,如上我是执行了 ls 命令【注意,在 at 中,最后都使用绝对路径,避免出错】,最后ctrl +d
结束输入,就会输出当前的任何信息:
job 3 at Thu Sep 9 21:20:00 2021
:任务 id 为3,后面跟执行时刻
但是并没有任何输出,当然,我们没文件嘛,就算输出也看不到,
2:那我们创建一个文件,然后再创建一个 at 任务:
[root@localhost at_dir]# touch hahaha.txt
[root@localhost at_dir]# at now + 1 minutes
at> ls /root/at_dir
at> <EOT>
job 4 at Thu Sep 9 21:22:00 2021
1分钟后,按下任意键【如回车】可以看到以下信息,atd 会将结果以邮件的形式告诉执行者:
您在 /var/spool/mail/root 中有新邮件
查看该文件在最后可以看到如下内容:
# 以下是邮件的相关信息
From [email protected] Thu Sep 9 21:24:03 2021
Return-Path: <[email protected]>
X-Original-To: root
Delivered-To: [email protected]
Received: by localhost.localdomain (Postfix, from userid 0)
id 467AD4025FB7; Thu, 9 Sep 2021 21:24:03 +0800 (CST)
Subject: Output from your job 5 # 这里是主题,从id 为5的job中输出的
To: [email protected]
Message-Id: <20210909132403[email protected]>
Date: Thu, 9 Sep 2021 21:24:03 +0800 (CST)
From: [email protected] (root)
hahaha.txt # 这里就是我们 job 的执行结果
3:如果想其没有输出的时候也发送邮件,就可以使用-m 参数:
# 删除对应目录下的文件后再执行以下任务
[root@localhost mail]# at -m now + 1 minutes
at> ls /root/at_dir
at> <EOT>
job 7 at Thu Sep 9 21:31:00 202
这时就可看到邮件了,虽然没有内容:
From [email protected] Thu Sep 9 21:31:00 2021
Return-Path: <[email protected]>
X-Original-To: root
Delivered-To: [email protected]
Received: by localhost.localdomain (Postfix, from userid 0)
id 735904025FB6; Thu, 9 Sep 2021 21:31:00 +0800 (CST)
Subject: Output from your job 7 # 来自新建的 job7
To: [email protected]
Message-Id: <20210909133100[email protected]>
Date: Thu, 9 Sep 2021 21:31:00 +0800 (CST)
From: [email protected] (root)
batch 命令就是使用的 at 命令,只不过 batch 会帮助我们在系统的 CPU 负载低于0.8时才会运行我们的任务,因此也不需要指定时间了。
[root@localhost mail]# batch
at> ls ~
at> <EOT>
job 13 at Thu Sep 9 21:53:00 2021
这里的例子都寄件非常简单,重点在于 at 命令的使用
[root@localhost mail]# uptime
21:55:36 up 4 days, 15:19, 1 user, load average: 0.00, 0.01, 0.05
后面的三个数据分别代表1分钟、5分钟和15分钟的平均负载
由 crond 服务提供,也是基于 cron 表达式,只不过 Linux 中最小支持分钟,最大支持周。
同 at 一样,提供了如下两个文件:
用户xxx执行 crontab 建立任务后,任务记录到/var/spool/cron/xxx
目录中,因此这种方式针对用户来创建任务的。
任务日志会被记录到/var/log/cron
中
crontab [-u username] [ -l | -e | -r ]
-u
该参数只有 root 可以使用,帮助 username 创建某个任务
-l
查看 crontab 内容
-e
编辑任务内容【可以看到所有已编辑任务,因此如果要删除某个任务的话,可以直接通过-e 手动删除】
-r
删除所有 crontab 任务内容
任务编辑格式:
* * * * 执行的任务
前面4个星号其实就是 cron 表达式,其取值按顺序分别如下:
分钟
0~59
小时
0~23
日期
1~31
月
1~12
周
0~7
特殊符号
任何。如0 12 * * *
就代表每天的12点0分执行。因为只限定了小时和分钟,日期和月都没有限制
,
分隔时间。如,每天12点、12点05、12点10分执行:0,5,10 12 * * *
时间段。
如:每天8点到12点的整点执行一次:0 8-12 * * *
/n
每隔 n 单位执行一次。
如每5分钟执行一次:*/5 * * * *
每12小时执行一次: 0 */12 * * *
切换到 xiaoyunshi 用户然后执行以下命令,进入vi模式的任务编辑:
[xiaoyunshi@localhost ~]$ crontab -e
*/1 * * * * date
如上任务很简单,每分钟执行一次 date 命令,这时我们就可以在/var/spool/cron/xiaoyunshi
中收到任务执行的结果邮件:
Return-Path: <[email protected]>
X-Original-To: xiaoyunshi
Delivered-To: [email protected]
Received: by localhost.localdomain (Postfix, from userid 1000)
id DD60A4025FB9; Sat, 11 Sep 2021 10:18:01 +0800 (CST)
From: "(Cron Daemon)" <[email protected]>
To: [email protected]
Subject: Cron <xiaoyunshi@localhost> date
Content-Type: text/plain; charset=UTF-8
Auto-Submitted: auto-generated
Precedence: bulk
X-Cron-Env: <XDG_SESSION_ID=4>
X-Cron-Env: <XDG_RUNTIME_DIR=/run/user/1000>
X-Cron-Env: <LANG=zh_CN.UTF-8>
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <HOME=/home/xiaoyunshi>
X-Cron-Env: <PATH=/usr/bin:/bin>
X-Cron-Env: <LOGNAME=xiaoyunshi>
X-Cron-Env: <USER=xiaoyunshi>
Message-Id: <20210911021801[email protected]>
Date: Sat, 11 Sep 2021 10:18:01 +0800 (CST)
2021年 09月 11日 星期六 10:18:01 CST
[xiaoyunshi@localhost ~]$ crontab -l
*/1 * * * * date
[xiaoyunshi@localhost ~]$ crontab -r
# 再次查看,已经没有任何任务了
[xiaoyunshi@localhost ~]$ crontab -l
no crontab for xiaoyunshi
一定要注意,-r 是删除所有任务
如果要创建系统维护相关的任务,则需要在/etc/crontab
文件中进行任务编辑。
crond 服务会每分钟读取一次/etc/crontab
文件中的任务和刚刚的用户创建的任务文件内容:/var/spool/cron/xxx
。
如果编辑完/etc/crontab
文件后任务没有执行,则可能该系统是先读取到内存中的,因此要重新启动下 crond 服务。
该文件内容如下:
SHELL=/bin/bash # 使用的 shell
PATH=/sbin:/bin:/usr/sbin:/usr/bin # 执行文件查找的路径
MAILTO=root # 接收邮件推送的用户
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# 下面就是该任务创建的语法
# * * * * * user-name command to be executed
* * * * * user-name command to be executed
,这个是该文件的任务创建语法。
比crontab -e
的方式多了个 user-name 参数,因为该文件一般是由 root 来创建系统任务的嘛,因此创建的任务需要指定任务的执行者是谁,而crontab -e
默认的执行者就是当前用户,因此不需要该参数。
每个任务在此编辑即可,这里就不演示了,方法和 crontab -e 一样。
该目录主要用于配置一些自己开发软件时需要用的任务。
默认有一个0hourly
文件:
# Run the hourly jobs
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
01 * * * * root run-parts /etc/cron.hourly
该文件内容主要执行最后的命令:每分钟调用一次run-parts
脚本来执行/etc/cron.hourly
目录下的任务。
run-parts
是一个脚本,会在5分钟内随机选一个时间来执行/etc/cron.hourly
目录下的执行文件
[root@localhost cron.d]# whereis run-parts
run-parts: /usr/bin/run-parts
接着看一下/etc 下和 cron 有关的目录:
[root@localhost etc]# ls /cron
cron.d/ cron.deny cron.monthly/ cron.weekly/
cron.daily/ cron.hourly/ crontab
除了 crontab
和cron.d
外还针对小时、天、周和月分别维护了对应的目录。
其中cron.hourly
下的执行文件就由上面的 run-parts 会在每小时的1-5分钟内调用,因此我们可以直接将每小时要执行的命令脚本写在该目录下。
其他几个目录是由 anacron
执行。
主要用于执行一些由于关机而无法执行的任务以及某些特殊原因导致没有在指定时间执行的任务。
anacron 是一个程序,会每小时执行一次,因此其配置文件就在/etc/cron.hourly/0anacron
中:
#!/bin/sh
# Check whether 0anacron was run today already
if test -r /var/spool/anacron/cron.daily; then
day=`cat /var/spool/anacron/cron.daily`
fi
if [ `date +%Y%m%d` = "$day" ]; then
exit 0;
fi
# Do not run jobs when on battery power
if test -x /usr/bin/on_ac_power; then
/usr/bin/on_ac_power >/dev/null 2>&1
if test $? -eq 1; then
exit 0
fi
fi
/usr/sbin/anacron -s
前面都是校验上一次执行 anacron 的时间戳,在最后执行了anacron -s
命令。
语法:
anacron -[sfn] [jobname]
-s
根据时间记录文件的时间戳判断是否需要执行各个 jobname
-f
强制执行,不看时间戳
-n
理科执行未执行的任务,不按延迟时间等待
job
在 /etc/anacrontab
定义的任务名
因此,anacrontab 的执行,是根据/etc/anacrontab
文件的配置来的:
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45 # 随机设置最大的延迟时间(min)
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22 #任务将延迟执行的时间
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly
#相差的天数 # 延迟时间 # 任务名 # 执行的命令
天数和/var/spool/anacron
目录记录的时间戳有关:
[root@localhost anacron]# ll /var/spool/anacron/
总用量 12
-rw-------. 1 root root 9 9月 11 11:41 cron.daily
-rw-------. 1 root root 9 8月 22 10:59 cron.monthly
-rw-------. 1 root root 9 9月 9 21:38 cron.weekly
[root@localhost anacron]# cat cron.daily
20210911
这三个文件记录了对应目录最近一次执行 anacron 的时间,如 cron.daily 上次执行 anacron 的时间为20210911.
因此 anacron 执行流程如下:
以 daily 为例。
/etc/anacrontab
中知道cron.daily
如果与上次执行 anacron 时间相差一天及以上,则执行run-parts /etc/cron.daily
/var/spool/anacron/cron.daily
记录的时间戳和当前时间比较,如果相差超过1天,就执行后面的命令,否则不执行/etc/at.allow
和/etc/at.deny
两个文件控制 at 使用权限/etc/cron.allow
和/etc/cron.deny
两个文件控制 at 使用权限;crontab -e
创建任务,创建的任务记录在/var/spool/cron/用户名
文件中;/etc/crontab
文件中创建,语法为* * * * * 执行者 执行的命令;
/etc/cron.d
目录创建,语法同上;/etc/cron.hourly/
下的任务由/etc/cron.d/0hourly
执行;/etc/cron.hourly/0anacron
调用,通过/etc/anacrontab
配置文件,结合/var/spool/anacron/
目录下对应的时间戳文件判断某个任务是否进行调用/etc/crontab
中创建的任务,如果因为关机或其他原因没有按时执行,则就不会再执行了,而/etc/cron.daily
、/etc/cron.weekly
和/etc/cron.monthly
中超时的任务就可以通过 anacron 程序在下次开机时重新调用。