cron (简体中文)
摘自 Wikipedia:
- cron 是一个在 Unix 及类似操作系统上执行计划任务的程序。cron 使用户能够安排工作(命令或shell脚本)在特定时间、日期或间隔定期运行,通常用于系统的自动化维护或者管理。
安装
cron 有多个实现程序,但是基础系统默认使用 systemd 的 Timers,下面的实现都不被默认安装。Gentoo Linux Cron 指南 提供了一个这些实现之间的比较。
可用的包:
- cronie
- fcron
- dcronAUR
- vixie-cronAUR
- scron-gitAUR
配置
激活及开机启动
安装后,默认的守护进程不会启动。安装的软件包通常提供了可以用 systemctl 控制的服务文件。例如 cronie 使用 cronie.service
。
/etc/cron.daily/
和类似的目录包含当前的任务,启动 cron 服务时会触发所有这类任务。
0anacron
任务,每小时执行一次,它允许延迟运行其他某些任务,比如因为未开机而延迟的任务。处理任务中的错误
cron 会记录 stdout 和 stderr 的输出并尝试通过 sendmail
命令发送邮件给用户。如果 Cronie 未找到 /usr/bin/sendmail
,则会禁用邮件通知。要发送邮件到用户的 spool,需要在系统上运行一个 smtp 守护进程,例如 opensmtpd。也可以安装提供 sendmail 命令的软件包,然后配置成通过外部邮件服务器发送邮件。或者使用 -m
选项将错误记录到日志并通过定制的脚本进行处理。
使用 ssmtp 的例子
ssmtp 是一个仅包含发送功能的 sendmail 模拟器,可以从本地计算机向 smtp 服务器发送邮件。尽管目前已经没有活跃维护者,这个程序依然是发送邮件的最简单方式。不需要运行守护进程,配置可以简单到只需在一个配置文件中编辑三行即可(如果你的主机是受信任的,可以通过你的邮件服务提供商转发未经认证的邮件)。ssmtp 无法收取邮件、展开别名或管理队列。
安装 ssmtpAUR,安装时会创建链接 /usr/bin/sendmail
指向 /usr/bin/ssmtp
。安装后编辑 /etc/ssmtp/ssmtp.conf
配置文件。详情请参考 ssmtp,到 /usr/bin/sendmail
的软链接可以确保 S-nail 等提供 /usr/bin/mail
的程序可以无需修改直接使用。
安装配置完成后重启 cronie
以确保 cronie 能够检测到新配置的 /usr/bin/sendmail
命令。
使用 msmtp 的例子
安装 msmtp-mta, 安装时会创建链接 /usr/bin/sendmail
指向 /usr/bin/msmtp
。重启 cronie
以确保 cronie 能够检测到新配置的 /usr/bin/sendmail
命令。你必须提供一种方法让 msmtp
能够将你的用户名转换成电子邮件地址。
然后要么在你的 crontab 中添加 MAILTO
行:
[email protected]
要么 创建 /etc/msmtprc
文件,并添加这一行:
aliases /etc/aliases
并创建 /etc/aliases
文件:
your_username: [email protected] # 可选: default: [email protected]
然后修改 cronie 的配置,将cronie 守护进程的 ExecStart
命令替换为
ExecStart=/usr/bin/crond -n -m '/usr/bin/msmtp -t'
使用 esmtp 的例子
安装完成后,进行如下配置:
/etc/esmtprc
identity myself@myisp.com hostname mail.myisp.com:25 username "myself" password "secret" starttls enabled default mda "/usr/bin/procmail -d %T"
Procmail 需要 root 权限才能在投递模式下工作,但如果你是以 root 身份运行 cronjobs,就不会有这个问题。
要测试一切是否正常工作,请创建一个内容为 "test message"
的 message.txt
文件。
在同一目录下运行:
$ sendmail user_name < message.txt
紧接着:
$ cat /var/spool/mail/user_name
现在您应该看到测试信息以及发送的时间和日期。
所有作业的错误输出现在会被重定向到 /var/spool/mail/user_name
。
由于权限问题,很难创建和发送邮件给root用户(比如 su -c ""
)。你可以要求 esmtp
将 root 的所有邮件转发给普通用户,方法是:
/etc/esmtprc
force_mda="user-name"
~/.esmtprc
中创建一个具有相同内容的本地配置。
运行下面的命令以确保它有正确的权限:
$ chmod 710 ~/.esmtprc然后用
message.txt
文件重新运行一遍上面的测试。使用 opensmtpd 的例子
安装 opensmtpd。
编辑 /etc/smtpd/smtpd.conf
。下面的配置允许本地投递:
listen on localhost action "local" mbox alias <aliases> match for local action "local"
您现在可以进行测试。运行 smtpd.service
,然后执行:
$ echo test | sendmail user
user 可以用任何 能够处理 mbox 格式的邮件阅读器来检查邮件,或者直接查看 /var/spool/mail/user
文件。如果一切都符合预期,您可以启用 openmtpd 使其开机运行。
这种方法的好处是不需要向远程服务器发送本地 cron 通知。但缺点是需要运行一个新的守护进程。
- 在写这篇文章的时候,Arch 的 opensmtpd 包还没有在
/var/spool/smtpd
下创建好所有需要的目录,但是守护进程会在需要指定所有者和权限时发出警告。只需按照警告创建即可。 - 尽管上文的配置中程序并不会接受远程连接,但使用iptables或类似的方法来阻止端口 25 也是预防措施。
运行时间很长的 cron 任务
假设 cron 调用了这个脚本:
#!/bin/sh echo "我有一个可恢复错误!" sleep 1h
这时会发生以下事件:
- cron 运行这个脚本
- 一旦 cron 检查到脚本有输出,就会启动你的邮件传输程序,并向通过管道其传递一些必要的头部内容。因为脚本还没有结束,还会有更多的输出,管道就没有关闭。
- 邮件传输程序打开到邮件服务器的链接,并等待后续的输出。
- 邮件服务器会在特定时间后关掉这个空闲的链接,你会受到类似这样的错误postfix closes the idle connection after less than an hour and you get an error like this :
smtpmsg='421 ... Error: timeout exceeded' errormsg='the server did not accept the mail' (服务器没有接受邮件)
要解决这个问题,你可以使用 moreutils 中的 chronic 或 sponge 命令。 它们各自的手册页面中写道:
- chronic(时序)
- chronic 运行一个命令时,会缓存它的标准输出和标准错误输出,并只在命令失败(返回值非零或崩溃)的情况下才会一并输出。如果命令成功执行,任何无关的输出将被隐藏。
- sponge(海绵)
- sponge 读取标准输入并将其写入指定的文件。不同于 shell 重定向,sponge 在打开输出文件之前会吸收所有的输入……如果没有指定输出文件,sponge 会输出到标准输出。
Chronic也会在打开标准输出之前缓冲命令输出。
Crontab 格式
crontab 的基本格式是:
分 时 日 月 星期 命令
- 分 值从 0 到 59。
- 时 值从 0 到 23。
- 日 值从 1 到 31。
- 月 值从 1 到 12。
- 星期 值从 0 到 6, 0 代表星期日。
空格用来分开字段,要微调你的时间表,也可以用下面特殊字符来设定范围:
符号 | 描述 |
---|---|
* | 通配符,表示所有支持的时间值 |
, | 用逗号分隔多个时间 |
- | 连接两个数值,给出一个范围 |
/ | 指定一个周期或频率 |
例如,下面一行:
*/5 9-16 * 1-5,9-12 1-5 ~/bin/i_love_cron.sh
将会在周内从早上 9 点到下午 4 点 55 分,每隔 5 分钟执行一次脚本 i_love_cron.sh
,夏季除外(6月、7月和8月)。
此外,crontab 还有一些特殊的关键字。
@reboot 启动时 @yearly 每年一次 @annually ( 同 @yearly) @monthly 每月一次 @weekly 每周一次 @daily 每天一次 @midnight (午夜,同 @daily) @hourly 每小时一次
例如:
@reboot ~/bin/i_love_cron.sh
将在启动时执行脚本 i_love_cron.sh
。
更多信息参见: https://www.adminschoice.com/crontab-quick-reference
更多的例子和高级配置技巧可以在下面找到。
基本命令
Crontabs 绝不应该被直接编辑;用户应该使用 crontab 程序来处理他们的 crontabs。为了能够访问这个命令,用户必须添加到 users 用户组(见 gpasswd 命令)。
要查看 crontabs,用户应该运行下面的命令:
$ crontab -l
要编辑 crontabs,可以使用:
$ crontab -e
要移除 crontabs, 可以使用:
$ crontab -r
如果用户有一个保存好的 crontab 想要用它完全覆盖旧的 crontab,可以使用:
$ crontab saved_crontab_filename
想从命令行(Wikipedia:stdin)覆盖一个 crontab,使用:
$ crontab -
想编辑别的用户的 crontab, 使用root运行下面的命令:
# crontab -u username -e
同一个格式(在命令后追加 -u username
)也可以用来列出或删除 crontabs。
范例
下面的条目:
01 * * * * /bin/echo Hello, world!
将会在每个月的每一天的每一个小时的第一分钟(例如,在12:01,1:01,2:01等)执行命令 /bin/echo Hello, world!
类似地,
*/5 * * jan mon-fri /bin/echo Hello, world!
将会在一月的每个工作日每五分钟(例如,在12:00,12:05,12:10等)执行一次相同的命令。
和前文 Crontab 格式一章相同,这一行
*0,*5 9-16 * 1-5,9-12 1-5 /home/user/bin/i_love_cron.sh
将会在周内从早上 9 点到下午 4 点 55 分,每隔 5 分钟执行一次脚本 i_love_cron.sh
,夏季除外(6月、7月和8月)。
也可以像这样输入周期性设置:
# Chronological table of program loadings # Edit with "crontab" for proper functionality, "man 5 crontab" for formatting # User: johndoe # mm hh DD MM W /path/progam [--option]... ( W = weekday: 0-6 [Sun=0] ) 21 01 * * * /usr/bin/systemctl hibernate @weekly $HOME/.local/bin/trash-empty
下面是一些不言自明的crontab语法例子:
30 4 echo "四点半了。" 0 22 echo "晚上十点了。" 30 15 25 12 echo "现在是圣诞节下午三点半。" 30 3 * * * echo "每天早上三点半提醒我。" 0 * * * * echo "新的一个小时到来了。" 0 6 1,15 * * echo "每月1号和15号的早上六点。" 0 6 * * 2,3,5 echo "周二三四的早上六点。" 59 23 * * 1-5 echo "周内每天的最后一分钟。" 0 */2 * * * echo "每两个小时。" 0 20 * * 4 echo "周四的晚上八点。" 0 20 * * Thu echo "周四的晚上八点。" */15 9-17 * * 2-5 echo "周内朝九晚五的每一刻钟。" @yearly echo "新年好!"
默认编辑器
要修改默认编辑器,请在 shell 初始化脚本中定义 EDITOR
环境变量,如环境变量所述。
作为普通用户,需要使用 su
代替 sudo
来正确拉取环境变量:
$ su -c "crontab -e"
如果希望给这个命令取别名,因为 su 会在一个新启动的子 shell 中启动,为了防止一些以外的发生,需要用 printf
加一个任意字符串,来提醒你仍然在 root 下运行:
alias scron="su -c $(printf "%q " "crontab -e")"
运行基于 X.org 的应用程序
Cron 不在 X.org 下运行,因此它无法知道启动 X.org 应用程序所需的环境变量,因此必须显式定义。我们可以使用类似 xuserrun-gitAUR 这样的程序来完成:
17 02 * ... /usr/bin/xuserrun /usr/bin/xclock
或者可以手动定义它们(echo $DISPLAY
将给出当前的 DISPLAY 环境变量值):
17 02 * ... env DISPLAY=:0 /usr/bin/xclock
如果需要在 cron 中运行 notify-send 进行桌面通知,因为 notify-send 通过 dbus 发送值。需要告诉 dbus 连接到正确的总线。 通过检查 DBUS_SESSION_BUS_ADDRESS 环境变量,并设置为相同的值,就可以找到地址。因此:
17 02 * ... env DBUS_SESSION_BUS_ADDRESS=your-address notify-send 'Foo bar'
如果是通过SSH完成的,需要给与权限:
# xhost +si:localuser:$(whoami)
异步任务处理
如果你经常关机,但又不想错过任务的执行,这里有一些解决方案(从最简单到最难):
Cronie
cronie 内含 anacron。其项目主页介绍道:
Cronie 包含了标准的 UNIX 守护进程 crond,它可以在预定的时间运行指定的程序和相关工具。 它基于最初的 cron,并增强了安全性和配置功能,比如可以使用 pam 和 SELinux。
Dcron
Vanilla dcronAUR 支持异步任务处理。只要用@hourly、@daily、@weekly或者@monthly加上任务名就可以了:
@hourly ID=greatest_ever_job echo This job is very useful.
Cronwhip
cronwhipAUR 是一个自动运行遗漏的 cron 任务的脚本。它与以前的默认 cron 实现 dcron 一起工作。 另见这个论坛帖子。
Anacron
Anacron 是 dcron 的完全替代者,它可以异步处理任务。
它由 cronie 提供,通过 /etc/anacrontab
进行配置。关于格式的信息可以在 anacrontab(5)
man page 中找到。运行 anacron -T
可以测试 /etc/anacrontab
的有效性。
Fcron
和anacron一样,fcron 假设计算机并不总是在运行。但与anacron不同的是,它可以在比一天更短的时间内安排这些事件,这对于经常暂停/休眠的系统(例如笔记本电脑)可能很有用。和 cronwhip 一样, fcron 也可以运行那些本应在计算机停机期间运行的作业。
用 fcron 取代 cronie 时,spool 目录会变为 /var/spool/fcron
,并使用 fcrontab
命令代替 crontab 来编辑用户的 crontabs。这些 crontab 以二进制格式存储,其旁边的文本版本在 spool 目录中为 foo.orig。由于这种行为上的差异,任何手动编辑的用户 crontabs 可能需要进行调整。
一个快速的脚本小程序,可以帮助您将传统的用户 crontabs 转换为 fcron 格式:
cd /var/spool/cron && ( for ctab in *; do fcrontab ${ctab} -u ${ctab} done )
另见这个论坛主题。
确保排他性
如果您有可能运行很久的任务(比如说变化很多或者网速突然变慢,备份可能会偶尔运行很长时间),那么 flock
(util-linux)可以确保 cron 任务在同一时间点只有一个运行。
5,35 * * * * /usr/bin/flock -n /tmp/lock.backup /root/make-backup.sh
Cronie
cronie 的相关文件层次结构如下:
/etc/ |----- cron.d/ | ----- 0hourly |----- cron.minutely/ |----- cron.hourly/ | ----- 0anacron |----- anacrontab |----- cron.daily/ |----- cron.monthly/ |----- cron.weekly/ |----- crontab |----- cron.deny
Cronie 提供了 cron 和 anacron 两种功能:只要系统在指定的时间可用,cron 以固定的时间间隔(粒度为一分钟)运行工作,,而anacron则在以天为单位指定的时间间隔执行命令。与 cron 不同的是,它并不假设系统连续运行。当系统启动时,anacron 就会检查是否有任何漏掉的任务,并进行相应的处理。
cron任务可以在 /etc/cron.d
目录中类似 crontab 的文件中定义,或者在 /etc/crontab
文件中添加。注意后者在默认情况下并不存在,但如果存在就会被使用。按照 /etc/cron.d/0hourly
的内容,/etc/cron.hourly
中的任何可执行文件将每小时运行一次(默认为每小时的第1分钟),而在 /etc/cron.minutely
中的可执行文件将每分钟执行一次。这些可执行文件通常是 shell 脚本,也可以使用可执行文件的符号链接。
/etc/cron.deny
文件包括不允许使用 crontab 的用户列表,如果没有这个文件,只有 /etc/cron.allow
中列出的用户才能使用它。
Anacron的工作原理类似,通过执行根据所需的作业频率放置在 /etc/cron.daily
、/etc/cron.weekly
和 /etc/cron.monthly
目录下的文件,cron 作业 /etc/cron.hourly/0anacron
确保 anacron 每天运行一次,以执行其待办任务。
- Cronie 使用
run-parts
来执行不同目录下的脚本。文件名中不应该包含任何点号(.),因为run-parts
在默认模式下会忽略它们(参见run-parts(8))。名字必须只由大小写字母、数字、下划线和减号组成。 -
systemctl status cronie
的输出可能会显示诸如CAN'T OPEN (/etc/crontab): No such file or directory
的内容。您可以忽略这些内容,因为这个文件 cronie 不是必须的。 - Cronie 对
/etc/cron.d/0hourly
的权限要求很严格。如果/etc/cron.d/{hourly,weekly,daily}...
中的任务损坏或权限不正确,/etc/cron.d/0hourly
中的所有任务都不会被运行(包括 anacron 启动器)。pacman -Qkk cronie
可以显示是否有这样的问题。
>/dev/null 2>&1
,将输出重定向到/dev/null:
0 1 5 10 * /path/to/script.sh >/dev/null 2>&1您也可以在您的 crontab 文件中设置
MAILTO=""
变量来禁用全部电子邮件提醒。Dcron
cron守护进程会解析一个名为crontab
的配置文件。系统中的每个用户都可以维护一个单独的crontab文件来单独调度命令。root 用户的 crontab 用于调度全系统的任务(用户可以选择使用 /etc/crontab
或 /etc/cron.d
目录,这取决于他们选择的 cron 实现)。
The cron daemon parses a configuration file known as crontab
. Each user on the system can maintain a separate crontab file to schedule commands individually. The root user's crontab is used to schedule system-wide tasks (though users may opt to use /etc/crontab
or the /etc/cron.d
directory, depending on which cron implementation they choose).
/var/spool/cron/root
# Run command at a scheduled time # Edit this 'crontab -e' for error checking, man 1 crontab for acceptable format # <@freq> <tags and command> @hourly ID=sys-hourly /usr/sbin/run-cron /etc/cron.hourly @daily ID=sys-daily /usr/sbin/run-cron /etc/cron.daily @weekly ID=sys-weekly /usr/sbin/run-cron /etc/cron.weekly @monthly ID=sys-monthly /usr/sbin/run-cron /etc/cron.monthly # mm hh DD MM W /path/command (or tags) # W = week: 0-6, Sun=0 21 01 * * * /usr/bin/systemctl suspend
下面几行是 crontab 条目的一种可接受格式,即以空格分隔的字段:
- @period
- ID=jobname (这个是 dcron 独有的)
- command
crontab 条目的另一种标准格式是:
- minute
- hour
- day
- month
- day of week
- command
crontab文件本身通常存储为 /var/spool/cron/username
。例如,root 的 crontab 文件位于 /var/spool/cron/root
。
参见 crontab man page 获取更多信息和配置示例。
另请参见
- Gentoo Linux Cron 指南
- crontab.guru - cronjob 表达式在线编辑器。