一次性任务与周期性任务

在Linux系统中,一次性任务周期性任务是两种常见的任务调度方式,分别通过不同的工具实现。以下是详细说明:


一次性任务

用于执行一次后立即结束的任务,常用工具为 atbatch

at 命令

  • 功能:在指定时间运行一次任务。

  • 安装(部分系统需手动安装):

    sudo apt-get install at  # Debian/Ubuntu
    sudo yum install at      # CentOS/RHEL
  • 常用操作

    • 添加任务:

      echo "command" | at 14:30 2023-12-31   # 指定具体时间
      echo "command" | at now + 2 hours      # 2小时后执行
    • 查看任务队列:

      atq
    • 删除任务(通过任务ID):

      atrm <任务ID>

batch 命令

  • 功能:在系统负载较低时执行一次性任务(基于at实现)。

  • 示例

    echo "command" | batch

周期性任务

用于定期重复执行的任务,主要通过 cron 实现。

Cron 系统

  • 核心组件
    • cron 守护进程:后台服务,负责执行计划任务。
    • crontab 命令:用户管理自己的任务配置文件。
  • 配置文件位置
    • 用户级:/var/spool/cron/<用户名>(通过 crontab -e 编辑)。
    • 系统级:/etc/crontab/etc/cron.d/(需root权限)。

Crontab 语法

  • 基本格式

    * * * * * <命令>
    │ │ │ │ │
    │ │ │ │ └── 星期几 (0-7, 0和7均为周日)
    │ │ │ └──── 月份 (1-12)
    │ │ └────── 日 (1-31)
    │ └──────── 小时 (0-23)
    └────────── 分钟 (0-59)
  • 特殊字符

    • *:任意值。
    • ,:分隔多个时间点(如 1,3,5)。
    • -:范围(如 1-5)。
    • */n:每隔n单位(如 */10 表示每10分钟)。
  • 示例

    0 3 * * * /backup.sh       # 每天凌晨3点执行
    */5 * * * * /monitor.sh    # 每5分钟执行
    0 0 1 * * /report.sh       # 每月1日午夜执行

Crontab 管理命令

  • 编辑当前用户的任务:

    crontab -e
  • 查看任务列表:

    crontab -l
  • 删除所有任务:

    crontab -r

系统级Cron

  • 直接编辑 /etc/crontab 或添加脚本到以下目录:

    /etc/cron.hourly/   # 每小时
    /etc/cron.daily/    # 每天
    /etc/cron.weekly/   # 每周
    /etc/cron.monthly/  # 每月

注意事项

  1. 环境变量问题:Cron任务默认使用最小环境变量,建议使用绝对路径或在脚本中设置变量。
  2. 日志查看:Cron任务日志通常位于 /var/log/cron/var/log/syslog
  3. 权限控制
    • 限制用户使用Cron:通过 /etc/cron.allow/etc/cron.deny 文件。
    • at 的权限控制文件为 /etc/at.allow/etc/at.deny

通过任务计划,可以让系统自动的按时间或周期性任务执行任务

一次性任务

未来的某时间点执行一次任务

  • at 指定时间点,执行一次性任务
  • batch 系统自行选择空闲时间去执行此处指定的任务

at工具:

  • 由包 at 提供
  • 依赖与atd服务,需要启动才能实现at任务
  • at队列存放在/var/spool/at目录中
  • 执行任务时PATH变量的值和当前定义任务的用户身份一致
#命令格式
at [option] TIME

#常用选项
-V 显示版本信息
-t time   时间格式 [[CC]YY]MMDDhhmm[.ss] 
-l 列出指定队列中等待运行的作业;相当于atq
-d 删除指定的作业;相当于atrm
-c 查看具体作业任务
-f /path/file 指定的文件中读取任务
-m 当任务被完成之后,将给用户发送邮件,即使没有标准输出

#curl+d保存计划任务并退出前台,转到后台运行

使用方法:

#首先确认atd服务是否开启
[root@centos8 ~]$systemctl status atd.service 
● atd.service - Job spooling tools
   Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2021-06-02 20:03:57 CST; 4s ago
 Main PID: 12499 (atd)
    Tasks: 1 (limit: 5791)
   Memory: 492.0K
   CGroup: /system.slice/atd.service
           └─12499 /usr/sbin/atd -f

Jun 02 20:03:57 centos8 systemd[1]: Started Job spooling tools.


#使用方法
[root@centos8 ~]$at
Garbled time #需要加时间才能使用
[root@centos8 ~]$date
Wed Jun  2 20:05:48 CST 2021 #当前时间
[root@centos8 ~]$at 20:07
warning: commands will be executed using /bin/sh #默认以/bin/sh来运行
at> echo $PATH
at> touch /data/at.log
#curl+d退出前台,转到后台运行
at> <EOT>
job 2 at Wed Jun  2 20:07:00 2021
[root@centos8 ~]$at -l #查看后台运行的一次性任务
2	Wed Jun  2 20:07:00 2021 a root
[root@centos8 ~]$ll /data/at.log 
-rw-r--r-- 1 root root 0 Jun  2 20:07 /data/at.log

#重定向实现一次性计划任务1
[root@centos8 ~]$echo wall at job | at 22:00 #wall会以广播形式发送给连接终端的全部窗口
warning: commands will be executed using /bin/sh
job 5 at Wed Jun  2 22:00:00 2021
[root@centos8 ~]$at -l
5	Wed Jun  2 22:00:00 2021 a root

#重定向实现一次性计划任务2
[root@centos8 ~]$cat at.txt 
rm -f /data/*
[root@centos8 ~]$date
Wed Jun  2 21:38:15 CST 2021
[root@centos8 ~]$at 21:39 < at.txt 
warning: commands will be executed using /bin/sh
job 7 at Wed Jun  2 21:39:00 2021
[root@centos8 ~]$at -l
7	Wed Jun  2 21:39:00 2021 a root
[root@centos8 ~]$ls /data/
a  b  c
[root@centos8 ~]$ls /data/
[root@centos8 ~]$

时间格式

TIME:定义出什么时候进行 at 这项任务的时间

HH:MM [YYYY-mm-dd]
noon, midnight, teatime(4pm)
tomorrow
now+#{minutes,hours,days, OR weeks}

说明:

  • 作业执行命令的结果中的标准输出和错误以执行任务的用户身份发邮件通知给root

  • 默认CentOS 8 最小化安装没有安装邮件服务,需要自行安装

  • [root@centos8 ~]#dnf -y install postfix
    [root@centos8 ~]#systemctl enable --now postfix
    #[root@centos8 ~]#dnf -y install mailx
    [root@centos8 ~]$ss -ntl #25端口打开才说明开启了邮件服务
    State          Recv-Q         Send-Q                   Local Address:Port                   Peer Address:Port                  LISTEN         0              100                          127.0.0.1:25                          0.0.0.0:*         
  • at任务即使重启也不会消失,因为是存在文件中:

[root@centos8 ~]$ll /var/spool/at/
total 4
-rwx------ 1 root root 2776 Jun  2 20:54 a00001019cae22 #at任务存放文件
drwx------ 2 root root    6 Jun  2 20:56 spool

黑名单

在黑名单里的用户将无法创建一次性的计划任务,但已经创建的计划任务不会收到影响

  • 白名单:/etc/at.allow 默认不存在,只有该文件中的用户才能执行at命令

  • 黑名单:/etc/at.deny 默认存在,拒绝该文件中用户执行at命令,而没有在at.deny 文件中的使用者则可执行

  • 如果两个文件都不存在,只有 root 可以执行 at 命令

  • 如果黑白名单中都存在同一个用户,则白名单优先级高,即允许这个用户执行at命令

[root@centos8 ~]$cat /etc/at.deny
azheng

[root@centos8 ~]$su - azheng

[azheng@centos8 ~]$at 22:10
You do not have permission to use at. #azheng用户无法创建计划任务了

周期性任务

注意事项

1、使用周期性任务时,一定要注意命令的路径,默认计划任务支持的PATH变量路径比较少,在计划任务的首行加入PATH变量,或者命令、脚本写绝对路径

2、cron任务中不建议使用%,它有特殊用途,它表示换行的特殊意义,且第一个%后的所有字符串会被将成当作命令的标准输入

如果在命令中要使用%,则需要用 \ 转义

注意:将%放置于单引号中是不支持的

30 2 * * * /bin/cp -a /etc/ /data/etc`date +\%F_\%T`
30 2 * * * /bin/cp -a /etc/ /data/etc`date +'%F_%T'`   #有问题

3、给计划任务写的脚本没有用的标准输出和标准错误要隐藏,否则会产生大量的垃圾邮件

4、计划任务的相关执行日志在/var/log/message 或 /var/log/cron

[root@aliyun ~]# tail -f /var/log/messages
Jun 26 13:56:01 aazheng systemd[1]: Started Session 63 of user root.
Jun 26 13:56:01 aazheng systemd[1]: session-63.scope: Succeeded.

周期性任务计划cron相关的程序包

  • cronie:主程序包,提供crond守护进程及相关辅助工具
  • crontabs:包含CentOS提供系统维护任务
[root@centos8 ~]$rpm -ql crontabs
/usr/bin/run-parts dir #此命令可以批量的执行脚本
  • cronie-anacron:cronie的补充程序,用于监控cronie任务执行状况,如:cronie中的任务在过去该运行的时间点未能正常运行,则anacron会随后启动一次此任务

使用前准备

  • 使用计划任务前需先确定服务是否开启
[azheng@centos8 ~]$systemctl status crond.service 
● crond.service - Command Scheduler
   Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2021-06-02 20:45:20 CST; 1h 41min ago
 Main PID: 880 (crond)
    Tasks: 1 (limit: 5791)
   Memory: 2.3M
   CGroup: /system.slice/crond.service
           └─880 /usr/sbin/crond -n

crond任务相关文件

/etc/crontab        #主配置文件
/etc/cron.d/        #子配置文件存放目录,建议将计划任务放在此目录下 便于管理
/etc/cron.hourly/   #小时执行脚本
/etc/cron.daily/    #日执行脚本
/etc/cron.weekly/   #星期执行脚本
/etc/cron.monthly/  #月执行脚本
/var/log/cron       #计划任务日志

cron任务配置范例

  • /etc/crontab 格式说明,详情参见 man 5 crontab
vim  /etc/cron.d/test #在子目录中配置计划任务,更便于管理
SHELL=/bin/bash #执行执行命令的shell
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin #最好和系统的PATH变量保持一致,否则可能有些命令无法执行
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
* * * * * azheng cmd #每分钟执行一次cmd
01 * * * * azheng cmd #每小时的第一分钟执行一次cmd
30 2 1-10,20 * 1-5 user cmd #每个月的周一到周五或者1到10号和20的两点三十分执行一次cmd(或的关系)
#/表示每
0 6-12/2 * 11 * /app/bin/test.sh #11月每天的6-12点之间每隔2小时执行/app/bin/test.sh
*/10 * * * * azheng cmd #每十分钟执行一次cmd
10-50/10 * * * * azheng cmd #每小时的10分到50分每隔10分钟执行一次cmd
0 0 1 * * etc_backup.sh #每月的第一天执行一次备份
* * 1 * * etc_backup.sh #每月的第一天,每分钟执行一次备份

0 0 * * * #每天晚上12点
0 23 * * * #每天的23点整
* */2 * * * #每隔两小时

crontab命令

  • 用户cron任务crontab

  • 保存在 /var/spool/cron/USERNAME,利用 crontab 命令管理

  • 每个用户都有专用的cron任务文件:/var/spool/cron/USERNAME

  • 默认标准输出和错误会被发邮件给对应的用户,如:wang创建的任务就发送至wang的邮箱

  • root能够修改其它用户的作业

  • 用户的cron 中默认 PATH=/usr/bin:/bin,如果使用其它路径,在任务文件的第一行加PATH=/path

crontab命令格式:

crontab [-u user] [-l | -r | -e] [-i]

crontab命令常用选项:

-l # 列出所有任务,但系统级别的计划任务无法列出?
-e # 编辑任务
-r # 移除所有任务
-i # 同-r一同使用,以交互式模式移除指定任务
-u # user 仅root可运行,指定用户管理cron任务

配置范例

[root@centos8 ~]$su - azheng
Last login: Wed Jun  2 22:05:35 CST 2021 on pts/0

[azheng@centos8 ~]$crontab -e
* * * * * cmd #因为已经切换用户,所以设置时就无需在指定用户名

[azheng@centos8 ~]$crontab -l
* * * * * /bin/ls

Ubuntu官方基础镜像中为什么直接将计划任务添加到/etc/cron.d目录下无法执行,而是需要crontab /etc/cron.d/time_sync.cron才能定期执行?

Ubuntu官方基础镜像中为什么直接将计划任务添加到/etc/cron.d目录下无法执行的原因是,这个目录是专门用于放置系统级别的cron jobs文件的,这些文件必须具有与其他系统级别的配置文件相同的正确权限。这些文件必须以root用户的身份创建,并具有正确的文件权限和文件格式。如果您在该目录下直接创建文件,则需要使用crontab命令进行手动加载文件,才能执行任务。这是因为该目录下的文件在系统级别上是没有自动加载的,您需要手动使用crontab命令将它们添加到cron守护进程中以执行任务。

但centos基础镜像不需要crontab /etc/cron.d/time_sync.cron也能执行阿

是的,CentOS基础镜像在将计划任务添加到/etc/cron.d目录下时,它们会自动加载和执行,而无需使用crontab命令来手动添加它们。这是因为在CentOS镜像中,/etc/cron.d目录中的文件会自动被/etc/crontab文件中的系统级别配置所包含,因此没有必要手动加载它们。

要注意的是,/etc/crontab文件中必须有匹配的行来包含/etc/cron.d目录中的cron jobs,以便它们能够被自动加载和执行。 如果您的CentOS环境中缺少这些匹配项,您可能需要手动添加它们,或使用适当的命令来重新生成/etc/crontab文件。

**并且通过 crontab 添加的计划任务还无需指定执行者的身份; 例如: /3 * * * * root /usr/sbin/ntpdate -b ${NTP_SERVER1} ${NTP_SERVER2} &» /var/log/time_sync.log,添加root执行的话,计划任务会报错 /bin/bash: root: command not found /3 * * * * /usr/sbin/ntpdate -b ${NTP_SERVER1} ${NTP_SERVER2} &» /var/log/time_sync.log,不添加执行者的身份就没事

您说得对:您不需要指定执行者的身份来运行cron jobs; crontab命令将自动使用当前用户的身份添加和管理cron jobs。

因此,当您使用crontab命令添加cron jobs时,不需要指定要运行命令的用户身份。而对于系统级别的cron jobs,例如在/etc/cron.d目录中的cron jobs,您需要使用root用户身份创建和管理这些文件。如果您尝试使用命令/bin/bash: root: command not found来运行root用户身份的cron jobs,则可能是因为您没有正确指定命令。 您应该使用以下命令来执行该cron jobs:sudo /usr/sbin/ntpdate -b ${NTP_SERVER1} ${NTP_SERVER2} &>> /var/log/time_sync.log,这将自动使用root用户的身份执行该命令。

控制用户执行计划任务:

/etc/cron.{allow,deny}

范例:无法加载宿主机环境变量的解决方法

故障背景

/etc/cron.d/time_sync

# Time synchronization
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
* * * * * root /scripts/time_sync.sh

/scripts/time_sync.sh

#!/bin/bash

# 根据环境变量设置NTP服务器指向 并将同步结果记录日志
/usr/sbin/ntpdate -b ${NTP_SERVER1} ${NTP_SERVER2} &>> /var/log/time_sync.log
  • 无法加载宿主机环境变量
# env | grep -i ntp
NTP_SERVER1=ntp.aliyun.com
NTP_SERVER2=ntp1.aliyun.com


# tail -f /var/log/time_sync.log
 9 Jun 10:48:01 ntpdate[66]: no servers can be used, exiting
 9 Jun 10:49:01 ntpdate[76]: no servers can be used, exiting
...


# 相当于未指向任何时间同步服务器就执行了
# /usr/sbin/ntpdate -b 
 9 Jun 11:01:44 ntpdate[163]: no servers can be used, exiting

解决方法

  • 这是因为计划任务在运行时并不会加载宿主机的环境变量。