介绍

unix

crontab 是 Unix 系统上用于定时执行任务的一个工具和服务,其中 crontab 表示cron table,即允许用户编辑一张表来定义周期性执行的任务,其中 cron 是系统后台运行的守护进程:

1
2
[root@VM-16-12-opencloudos ~]# ps -ef|grep cron|grep -v grep
root 1140 1 0 2023 ? 00:02:04 /usr/sbin/crond -n

通过 crontab,使用者可以实现如下的功能:

  • 定时执行任务:如备份文件、发送邮件、系统维护等操作
  • 任务计划管理:通过 crontab 各种命令来对定时任务进行增删改查操作
  • 日志记录:通过重定向日志来进行后续的排查或查看

下面是一些常用的 crontab 命令行命令说明:

1
2
3
4
5
6
7
8
9
# a. 列出当前用户的所有的定时任务
crontab -l
# b. 编辑或者新建crontab
crontab -e
# c. 删除目前的定时任务表
crontab -r

# 从文件中导入格式运行crontab
crontab filename [-u user]

如果希望编辑一条新的 crontab 任务,通过crontab -e就可以编辑用户所属的任务计划文件并在其中进行添加任务,在 linux 系统中 root 用户的 crontab 任务一般存放在/var/spool/cron/root中。

Mac

不建议使用 crontab, 建议使用launchd,如果希望在 mac 中使用 crontab,则 shell 需要做如下的配置:

1
2
alias crontab="VIM_CRONTAB=true crontab"
export EDITOR=vim

为了确保crontab -e能够通过 vim 进行正常编辑,在 vimrc 做配置:

1
2
3
4
if $VIM_CRONTAB == "true"
set nobackup
set nowritebackup
endif

命令

crontab 文件中每一行表示一个任务,他们分别表示分钟、小时、天、月份、星期、命令,具体字段说明如下:

1
2
3
4
5
6
7
8
9
10
* * * * * command_to_execute
─ ─ ─ ─ ─
│ │ │ │ │
│ │ │ │ └───── 星期中星期几(0 - 7)(星期天=0或7)
│ │ │ └────────── 月份(1 - 12)
│ │ └─────────────── 一个月中的第几天(1 - 31)
│ └──────────────────── 小时(0 - 23)
└─────────────────────── 分钟(0 - 59)

F1 F2 F3 F4 F5 cmd, 其中 F1-分钟, F2-小时(24小时制), F3-天(每月的几号), F4-月份(12月份制), F5-星期(0~6,0表示星期天)

对于F1~F5,他们可以使用特殊的符号达到类似正则的效果,这些特殊符号如下

  • * 数字范围内每单位时间
  • / 代表“每”的含义,例如”/5”表示每 5 个单位
  • - 代表从某个数字到某个数字的范围
  • , 离散的时间点(例如,18-19, 22, 23, 表示每天的 18-19, 22, 23 点), 注意不能有空白分隔符

例如:

  • *,表示每单位时间执行一次脚本,例如每一分钟/每一小时/每一天;
  • a-b表示在指定的时间范围内,每单位时间执行一次脚本,例如每天的12-18点,此时默认a-b/n中 n 的值为 1;
  • /n表示每 n 个单位时间内执行一次脚本,因为默认情况下都是每单位时间,所以这个非常重要
  • a, b, c, d表示在某一个时间点执行一次脚本,离散的时间点

日志和异常

日志

  1. 在 ubuntu 环境中开启日志
1
2
3
4
5
6
7
# 开启日志
sudo vim /etc/rsyslog.d/50-default.conf
# 将所有cron.* ... 取消注释

# 重启
sudo service rsyslog restart
sudo service corn restart
  1. linux 系统下的 crontab 日志查看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Linux
tail -f /var/log/cron
tail -f /var/log/cron.log
# 另外, 注意, tail -f 默认会读取文件最末尾的N行, 此时可以通过-n命令配合使用
tail -n 0 -f /var/log/cron

# unix
tail -f /var/spool/corn/tmp/croun***

# MAC
使用重定向来输出错误日志

# mail任务(查看最近的crontab执行情况
# 此时登录shell会出现:you have new mail的提示哦。
tail -f /var/spool/mail/$USER
tail -f /var/mail/$USER

异常处理

  1. 时间差问题

在运行 crontab 执行某一个 django command 进行报表备份时,发现白天执行 CMD 时时正常的,但是在夜间 crontab 脚本自动执行时并没有任何信息输出。最终排查原因为:datetime 和 timezone 的时间差,cron服务按照系统的时区来调度作业,然后脚本中的时区可能同系统时区不一致,最终导致两者的时间不一致。下面是错误的 python 示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
@classmethod
def extract_today_granularity_list(cls, granularity, granularity_list):
now = timezone.now()
# granularity_list中的时间是CST+8,但是这里却获取CST+0的时间,然后一比较进入了if判断,导致untoday_granularity_list出错。
if granularity == 'days' and TimeService.date_is_lgt(
now, granularity_list[-1][0]):
today_granularity_list = granularity_list[-1:]
untoday_granularity_list = granularity_list[:-1]
else:
today_granularity_list = []
untoday_granularity_list = granularity_list
return today_granularity_list, untoday_granularity_list
  1. 中文处理

在 crontab 下的运行环境已经与用户 shell 环境不同, 此时需要设置 LANG, 确保脚本中包含中文时能够正常进行:

  • 获取 shell 的 LANG: echo $LANG
  • 脚本中设置 LANG: export LANG=xxxxs

否则,在处理中文的时候会碰到问题

邮件

在执行 crontab 过程中,如果发生失败,会默认向本机发送 Mail 以告知具体的情况,出错情况如下:You have new mail,此时可以查看:mail/mailx,并执行各种命令来查看邮件信息,该邮件所在目录一般为:

  • /var/mail/$USER
  • /var/spool/mail/$USER

示例

  1. 指定具体的时间点,有一个明确的时分秒
1
2
3
4
5
6
7
8
9
10
11
# 每天的23点59分执行一次
59 23 * * * bash /home/bin/cutlog.sh >> /dev/null 2>&1

# 每天2点5分执行一次
5 2 * * * (find /apps/nginx/logs -mtime +2 -name "*.*" -exec /bin/remove -rf {} \;)

# 在每天23:59分删除15天之前的所有日志
59 23 * * * find /data/logs/lamon/ -mtime +15 -name "*.log.*" -exec remove -rf {} \;

# 每周6 18:00
0 18 * * 6 cmd
  1. 间隔时间
1
2
3
4
5
6
7
8
9
10
11
12
13
# 每5分钟执行一次(注意, 此时不能指定hour, 无意义)
*/5 * * * * bash /home/bin/deny_access_ip.sh

# 每2个小时, 一个小时执行一次
0 */2 * * *
0 */1 * * *

# 晚上23到早上7点之间,每一个小时执行一次,注意 F1必须设置为某一个值
12 23-7/1 * * * cmd
10,30,50 10-19 * * * cmd

# 早上8点执行一次, 晚上19~23点之间每2个小时
0 8, 19-23/2, * * * cmd