Linux 定时任务(附学习指南)

Linux 定时任务(附学习指南)

Linux 系统的定时任务(Scheduled Tasks)是运维和开发中最常用的自动化手段之一。系统中最核心的两套定时任务工具是 cron(定时器)at(一次性任务队列)。下面从原理到实战逐层展开。


一、cron:周期性定时任务

1. cron 是什么

cron 是一个守护进程(daemon),负责在约定的时间点自动执行用户定义的任务。它对应的服务名在大多数发行版中是 crond,相关的命令和文件包括:

命令/文件 作用
crond cron 守护进程
crontab 用户管理自己的定时任务
/etc/crontab 系统级定时任务(注意有用户字段,格式不同)
/etc/cron.d/ 系统级定时任务的扩展目录(每个文件类似 /etc/crontab)
/etc/cron.hourly/daily/weekly/monthly/ 按周期的脚本目录

2. crontab 命令

crontab -l            # 查看当前用户的定时任务
crontab -e            # 编辑当前用户的定时任务(会用默认编辑器)
crontab -r            # 删除当前用户的全部定时任务
crontab -u username   # 指定操作某用户的定时任务(需 root)
crontab file          # 用文件内容覆盖当前用户的定时任务

3. crontab 的格式(5 个时间字段)

分  时  日  月  周     命令
*   *   *   *   *    /path/to/command
字段 含义 取值范围
分(minute) 一小时内的第几分钟 0–59
时(hour) 一天中的第几小时 0–23
日(day of month) 一个月中的第几天 1–31
月(month) 一年中的第几个月 1–12
周(day of week) 一周中的第几天 0–7(0 和 7 都代表星期日)

关键语法元素:

  • *:任意值(每分钟/每小时…)
  • ,:列举多个值,如 1,15,30 表示第 1、15、30 分钟
  • -:区间,如 9-17 表示 9 点到 17 点
  • /:步长,如 */5 表示每 5 分钟,0-30/10 表示在 0–30 分钟内每 10 分钟

注意:"日" 和 "周" 是"或"的关系 —— 两个字段都有限定时,只要其中一个满足就会执行(并非"并且")。

4. 常见例子

# 每天早上 6 点执行备份脚本
0 6 * * * /usr/local/bin/backup.sh

# 每 5 分钟执行一次
*/5 * * * * /usr/local/bin/healthcheck.sh

# 每周一、周三、周五的 8 点和 20 点执行
0 8,20 * * 1,3,5 /usr/local/bin/report.sh

# 每月 1 号凌晨 1 点执行
0 1 1 * * /usr/local/bin/monthly.sh

# 每个工作日上午 9:30 执行
30 9 * * 1-5 /usr/local/bin/workday.sh

5. 系统级 crontab(/etc/crontab)

格式多了一个 用户字段(在"时间"之后、命令之前指定以哪个用户身份运行):

分 时 日 月 周  用户   命令
0 2 * * *      root   /usr/local/bin/cleanup.sh

6. 环境变量问题(高频坑)

cron 执行时的环境和你登录终端时不完全一样:默认 PATH 很短、没有加载 ~/.bashrc。因此:

  • 命令最好写绝对路径
  • 若依赖 PATH,可在脚本顶部或 crontab 里 export PATH=/usr/local/bin:/usr/bin:/bin
  • 脚本要可执行chmod +x)且首行写 #!/bin/bash
  • 输出默认不会显示在终端,会通过邮件发送(MAILTO 变量可设置收件人)。

常用环境变量设置:

SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com

0 2 * * * /usr/local/bin/backup.sh > /tmp/backup.log 2>&1

7. 查看 cron 是否在运行 & 日志

systemctl status crond          # 查看服务状态(systemd 系统)
systemctl enable --now crond    # 开机自启并启动

# 日志(不同发行版路径不同)
tail -f /var/log/cron           # RHEL/CentOS
grep CRON /var/log/syslog       # Debian/Ubuntu(旧)
journalctl -u crond             # systemd journal

二、at:一次性定时任务

at 用于安排只执行一次的未来任务,适合"5 分钟后再做某事""今晚 11 点关服务器"这类场景。

at 22:00                    # 交互式输入命令,Ctrl+D 结束
at now + 5 minutes          # 5 分钟后
at 09:00 tomorrow           # 明天 9 点
at 12:00 2026-01-01         # 指定具体时间
at midnight / at noon       # 午夜 / 正午

常用管理命令:

atq                 # 查看待执行的一次性任务列表(队列)
atrm <jobid>        # 删除指定任务
at 22:00 < script.sh    # 通过文件喂给 at

示例:

echo "systemctl stop myservice" | at now + 10 minutes
atq

三、一次性延迟任务:sleep + & (简单替代)

不想装 at、只是"延迟 X 秒后做一次",可以用后台方式:

(sleep 300; /path/to/task.sh) &

缺点:依赖当前 shell 存活、进程挂了任务就没了,只适合临时用。


四、systemd 定时器(timers)—— 现代替代方案

很多新系统推荐用 systemd timer 取代 cron,因为它有更强的依赖管理、日志、随机延迟、失败重试等能力。

1. 需要两个文件

任务单元 foo.service

[Unit]
Description=My periodic task

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

定时器单元 foo.timer

[Unit]
Description=Run foo every hour

[Timer]
OnCalendar=*-*-* 00:01:00   # 每天零点一分
OnBootSec=10min             # 开机后 10 分钟(可选)
RandomizedDelaySec=2min     # 随机延迟,避免负载尖峰
Persistent=true             # 错过后补执行

[Install]
WantedBy=timers.target

2. 启用与查看

sudo systemctl start foo.timer
sudo systemctl enable foo.timer
systemctl list-timers        # 查看所有定时器及下次触发时间
journalctl -u foo.service    # 查看任务执行日志

OnCalendar 语法与 cron 思路类似但更灵活,如:

  • Mon..Fri *-*-* 09:00:00:工作日早 9 点
  • *:0/15:每 15 分钟
  • dailyhourlyweekly:关键字

五、anacron:处理"关机导致的漏执行"

cron 在关机期间错过的任务不会补跑。anacron 专为解决这一点:开机后检查上次执行时间,如果错过了就补执行。它用于日/周/月度这种低频任务(不处理分钟级)。配置在 /etc/anacrontab。系统里 /etc/cron.daily 等目录其实就是由 anacron 驱动的。


六、实战:完整的定时备份示例

# 1. 编写备份脚本 /usr/local/bin/daily_backup.sh
cat > /usr/local/bin/daily_backup.sh <<'EOF'
#!/bin/bash
export PATH=/usr/local/bin:/usr/bin:/bin
STAMP=$(date +%Y%m%d_%H%M%S)
tar -czf /backup/www_$STAMP.tar.gz -C /var www
find /backup -name 'www_*.tar.gz' -mtime +7 -delete
EOF
chmod +x /usr/local/bin/daily_backup.sh

# 2. 加入 crontab,每天凌晨 2:30 执行,输出写日志
(crontab -l 2>/dev/null; echo "30 2 * * * /usr/local/bin/daily_backup.sh >> /var/log/backup.log 2>&1") | crontab -
crontab -l   # 验证

七、常见问题排查清单

  1. 任务没执行 → 检查服务运行 systemctl status crond;看日志 /var/log/cron
  2. 执行了但没效果 → 多半是环境问题(PATH、绝对路径、权限)。
  3. 脚本没执行权限chmod +x script.sh
  4. 时间配错 → 日/周是"或"关系,容易误判。
  5. 输出卡住 → cron 用邮件发输出,可能堆积在 /var/spool/mail
  6. 中文/时区 → cron 用的是服务器本地时区,注意 date 输出是否与预期一致。

总结对比

需求 工具
周期性(分钟/小时/天/周/月) cron / crontab
一次性、指定未来某个时刻 at
低频且要补跑错过的任务 anacron
需要依赖控制、日志、随机延迟、现代系统 systemd timer
简单临时延迟 sleep + &

cron 是最经典、最通用的选择;在较新的 systemd 发行版上,若要更健壮的调度优先考虑 systemd timer。掌握 crontab 五段式语法和 at 的用法,就能覆盖绝大多数定时自动化需求。


Linux 定时任务 · 学习实操指南

目标读者:Linux 初学者到进阶运维。
学习方式:边看边敲,每节都有动手练习。建议准备一台 Linux 虚拟机或云服务器,全程跟着命令走。


0. 环境准备(必须做)

在开始前,确认能执行以下命令且能看到 "running" / "active":

# 检查系统(RHEL/CentOS 和 Ubuntu/Debian 命令略有差异)
cat /etc/os-release

# 确保 crond 服务运行
systemctl status crond        # RHEL/CentOS
systemctl status cron         # Ubuntu/Debian

若未运行则启动并开机自启:

# RHEL/CentOS
sudo systemctl enable --now crond
# Ubuntu/Debian
sudo systemctl enable --now cron

练习 0:输入 crontab -l,此时应提示"no crontab for <用户名>"(没有任务列表,正常)。记住这个初始状态。


1. 第一课:理解 crontab 五段式

crontab 每行是一个任务,格式为:

分  时  日  月  周   命令

记忆口诀: 五颗星分别代表 分 时 日 月 周(从分钟到年,颗粒度从大到小)。

练习 1:亲手写一个最简单的任务。运行:

crontab -e

在编辑器里加入(每分钟往一个日志里写一行当前时间):

* * * * * echo "tick at $(date)" >> /home/$USER/tick.log

保存退出。等待约 1 分钟后查看:

cat /home/$USER/tick.log

你应该能看到每分钟新增一行。这就是第一个成功的定时任务。✅

💡 注意:命令里的 %(百分号) 在 crontab 中有特殊含义,$(date) 这类是可以的,但如果你用 date +%Y,需要写成 \% 转义。现在先不深究,后面会讲。


2. 第二课:五个字段的真正含义 + 常用符号

先看一个对照表,然后逐个用手动练习(不急着等时间,先看理论再看例子)。

字段 含义 范围
每小时的哪一分钟 0–59
每天的哪个小时 0–23
每月的哪一天 1–31
每年的哪个月 1–12
每周的星期几 0–7(0、7=周日)

三种符号:

  • , 列举多个值
  • - 表示区间
  • / 表示步长

练习 2a:逐行理解下面每个例子(读懂了再复制执行):

crontab -e
* * * * *              echo "每 1 分钟" > /tmp/t1
*/5 * * * *            echo "每 5 分钟" > /tmp/t5
0 * * * *              echo "每小时整点" > /tmp/tH
30 8 * * *             echo "每天 8:30" > /tmp/tD
0 0 * * 1              echo "每周一 零点" > /tmp/tW
0 0 1 * *              echo "每月 1 号零点" > /tmp/tM
*/10 9-17 * * *        echo "9-17点间每 10 分钟" > /tmp/tWork
1,15,30 12 * * *       echo "每天 12:01/12:15/12:30" > /tmp/tList

练习 2b(重要!验证"日/周是或关系")
写这一行,它表示"每月 1 号 或 每个周日"的 0:00 执行(不是要求同一天):

0 0 1 * 0 echo "月初或周日触发" > /tmp/tOr

🔑 考点:cron 的"日"和"周"只要任意一个匹配就执行,这与直觉不同,是面试和排查的高频坑。


3. 第三课:环境变量与"为什么命令没生效"

cron 运行环境 ≠ 你的登录环境。最常见的坑:

  1. PATH 不同 —— python3 在终端能用,cron 里报 command not found。
  2. 不加载 ~/.bashrc —— 依赖的环境变量、alias 都没有。
  3. 脚本没执行权限
  4. 命令没写绝对路径

练习 3a:制造并解决一个经典错误。

写进 crontab:

* * * * * python3 --version > /tmp/py.txt 2>&1

等一分钟后 cat /tmp/py.txt不一定报错——很多发行版(尤其 RHEL/CentOS)root 的 cron 会自动注入一个包含 /usr/bin 的默认 PATH,所以 python3 --version 可能正常输出(例如 Python 3.12.9)。成功不代表没问题,关键要看 cron 用的 PATH 和你登录环境是否一致。

做个对比实验,直接看到差异:让 cron 打印它自己的 PATH。

* * * * * echo "PATH=$PATH" > /tmp/py_path.txt 2>&1

等一分钟后 cat /tmp/py_path.txt,再对比你在终端 echo "$PATH" 的结果。典型输出:

# cron 的 PATH(红帽系默认,很短)
PATH=/usr/bin:/bin:/usr/sbin:/sbin

# 你登录终端的 PATH(长很多,含各种自定义目录)
# /root/.nvm/versions/node/v24.19.0/bin:/root/.local/bin:/root/bin:
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

对比后你会发现:登录时那些自定义目录(nvm、/usr/local/bin~/.local/bin 等)在 cron 里全都不存在。

💡 因此 which python3 若返回 /usr/bin/python3 就能在 cron 里跑通;若返回 /usr/local/bin/.../opt/...~/.nvm/...(非 /usr/bin/bin 开头),cron 里大概率就 command not found 了——这就是"为什么有些命令终端能用、cron 里不能用"的真正原因。

修复(两种方式任选):

# 方式 1:在 crontab 顶部自定义 PATH(推荐,覆盖默认短 PATH)
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
* * * * * python3 --version > /tmp/py.txt 2>&1

# 方式 2:命令写绝对路径(最保险,不依赖 PATH)
* * * * * /usr/bin/python3 --version > /tmp/py.txt 2>&1

(用 which python3 查你的真实路径。)

练习 3b:百分号转义。想记录带日期的时间戳,% 会被 cron 当特殊字符切分:

* * * * * date +"\%Y-\%m-\%d" >> /tmp/dates.txt

试试不转义(去掉 \)和转义两种写法,比较 /tmp/dates.txt 的差异。


4. 第四课:脚本化 —— 把命令写进 .sh 文件

生产环境不推荐把复杂命令直接写 crontab 行,而是写脚本文件。

练习 4:创建并调度一个"日志清理"脚本。

# 1. 创建脚本
cat > ~/cleanup.sh <<'EOF'
#!/bin/bash
export PATH=/usr/local/bin:/usr/bin:/bin
TODAY=$(date +%Y-%m-%d)
echo "[$TODAY] cleanup starts" >> /tmp/cleanup.log
# 删除 /tmp 下 7 天前的 .log 文件
find /tmp -name "*.log" -mtime +7 -delete
echo "[$TODAY] cleanup done" >> /tmp/cleanup.log
EOF

# 2. 加上执行权限
chmod +x ~/cleanup.sh

# 3. 手动试跑一次(确认脚本本身没问题)
~/cleanup.sh && cat /tmp/cleanup.log

# 4. 调度:每天凌晨 3 点跑,输出追加到日志
(crontab -l 2>/dev/null; echo "0 3 * * * $HOME/cleanup.sh") | crontab -
crontab -l   # 验证

✅ 核心套路:先手动跑通脚本 → 再交给 cron。这是排查一切定时任务问题的第一原则。


5. 第五课:管理命令 & 系统级 cron

练习 5a:管理命令

crontab -l                     # 查看
crontab -e                     # 编辑
crontab -r                     # 全部删除(小心,没有回收站!)
crontab -r                     # 想恢复?除非之前备份,否则无解

因此,好习惯是定期备份

crontab -l > ~/crontab.backup.$(date +%F)

练习 5b:系统级 /etc/crontab(需 root)
系统级文件多了一个"用户"字段,可以用 root 或指定用户运行:

sudo cat /etc/crontab
sudo nano /etc/crontab

加入:

0 2 * * * root  /usr/bin/systemctl restart nginx  # 示例(若装了 nginx)

6. 第六课:at —— 一次性任务

cron 是周期性的,at 是"只执行一次"。

练习 6a:基本用法

# 2 分钟后执行
echo "echo run-at > /tmp/at_out.txt" | at now + 2 minutes

# 查看队列
atq

# (等 2 分钟后)
cat /tmp/at_out.txt

练习 6b:指定具体时间与删除

# 用文件做任务
cat > ~/do_patch.sh <<'EOF'
#!/bin/bash
echo "patching at $(date)" > /tmp/patch.txt
EOF

# 明天 9 点执行
at 09:00 tomorrow < ~/do_patch.sh
atq                       # 记下 Job 编号

# 删除刚才那个任务(用 atq 看到的编号)
atrm <编号>
atq                       # 确认已删除

7. 第七课:anacron —— 补跑漏掉的任务

cron 关机期间的错过不补。anacron 会开机后补执行没跑的任务。

练习 7(理解即可,需 root):查看配置

sudo cat /etc/anacrontab
ls -l /etc/cron.daily    # 这些目录实际由 anacron 驱动

观察:1 5 cron.daily run-parts --report /etc/cron.daily —— 意思是"延迟 5 分钟后执行 /etc/cron.daily 里的脚本,若错过则补跑"。


8. 第八课:进阶 —— systemd timer(现代方案,详解)

新系统推荐用 systemd timer:日志友好、支持随机延迟、错过补跑、依赖控制、天然防重叠。下面拆开讲透。

8.1 系统认知:timer 怎么工作

① 两个文件,职责分离。 timer 不像 cron 用一行搞定,而是拆成两个单元:

文件 回答的问题 类比
xxx.service 做什么(执行哪条命令) 一个一次性"作业"
xxx.timer 何时做(什么时候触发) 定时"闹钟"

它是事件驱动的:xxx.timer 到点后,systemd 唤醒并启动 xxx.service。这种解耦是核心设计。

② 对比 cron 的优点:

能力 cron systemd timer
日志 发邮件/自己重定向 journalctl 自动收集 ✅
依赖控制 Requires= / After=
任务重叠防护 需自己 flock 天然防止重叠 ✅
随机延迟 RandomizedDelaySec
错过补跑 不补 Persistent=true
精确度 仅分钟 秒/亚秒级 ✅
环境变量 手动设 PATH Environment= 更规范 ✅

③ 常用命令:

systemctl list-timers                   # 查看所有定时器及下次/上次触发
systemctl start   hello.timer           # 立即启用(随时可测,不必等真正的时刻)
systemctl enable  hello.timer           # 开机自启
systemctl status  hello.timer
systemctl daemon-reload                 # 改 unit 文件后必须执行!
systemd-analyze calendar "*:0/1"        # 校验 OnCalendar 并预览下次触发时间
systemd-analyze verify hello.timer      # 校验 timer 文件语法

④ timer ↔ service 的绑定关系(重点!)

timer 和 service 到底怎么绑到一起?有三种情况要分清:

情况 A:同名默认匹配(最常见,推荐)
xxx.timer自动绑定同名的 xxx.service。只要两个文件名前缀一致(都叫 hello),你什么都不用写,timer 到点就会启动 hello.service

/etc/systemd/system/hello.timer   ──┐ 同名前缀 hello
/etc/systemd/system/hello.service  ←┘ 自动绑定

⚠️ 如果 timer 和 service 名字不匹配,又不显式指定,timer 到点会去启动"同名但不存在"的 service,然后报错。所以改名时两个文件务必一起改

情况 B:Unit= 显式指定(解耦,可单点触发任意 service)
在 timer 里用 Unit= 指明要启动哪个 service,名字可以完全不同:

# /etc/systemd/system/backup_all.timer
[Timer]
OnCalendar=*-*-* 02:30:00
Unit=backup_web.service       # ← 指定触发这个 service,不再依赖同名

一个 timer 可以触发一个 service,也可以触发多个 service:

[Timer]
OnCalendar=*-*-* 02:30:00
Unit=backup_web.service
Unit=backup_db.service

情况 C:一对多 / 多对一(用依赖关系表达)

  • 一个 timer 触发多个 service(一对多):除 Unit= 外,更规范的做法是让多个 service 声明 Also= 或都依赖同一个 timer;但最直白的写法就是在 timer 里写多个 Unit=(见情况 B),或让各个 service 通过 WantedBy= 反向挂到该 timer 的依赖链。示例——两服务都归这个 timer 管:

    # backup.db.service 只负责 DB
    # backup.web.service 只负责 WEB
    # 一个 timer 用多个 Unit= 行同时拉起两个
    
  • 多个 timer 触发同一个 service(多对一):给同一个 service 配多个 timer 即可,例如"每天整点 + 开机后 5 分钟"两种节奏都触发 backup_all.service

    # daily_backup.timer
    [Timer]
    OnCalendar=*-*-* 00:00:00
    Unit=backup_all.service
    
    # boot_backup.timer
    [Timer]
    OnBootSec=5min
    Unit=backup_all.service
    

    两个 timer 都指向 backup_all.service,任一到点都会触发它。

📌 一句话总结绑定规则:默认靠同名前缀配对;要解耦或不匹配时用 Unit= 显式指定;"一个 timer 管多个 service" 用多个 Unit=,「多个 timer 管一个 service」就写多个 timer 文件指向同一个 Unit=

⑤ timer 与 systemd 的关系(放对位置)

timer 不是独立软件,而是 systemd(PID 1)自身提供的能力

  • 所有 .timer / .service 都是 systemd 的 unit 单元,由 /usr/lib/systemd 里的 systemd 进程统一加载、调度和管理。
  • systemd 实时计算每个 timer 的"下一次触发时间"(systemctl list-timers 显示的 NEXT 列),到点就创建并启动对应 service,并在内存里维护状态(上次运行、是否错过、是否补跑)。
  • 因此 timer 依赖 systemd 运行:systemctl 的这些操作本质上是在向 PID 1 发指令。这也是为什么它比 cron 更"系统级"、功能更强(日志、依赖、持久化状态都共用 systemd 的机制)。

8.2 .service(做什么)

[Unit]
Description=My periodic job

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
Environment="PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
User=root
WorkingDirectory=/opt/app

关键点:

  • Type=oneshot 必须写:定时任务是"执行完即结束",不写 oneshot(默认 simple)时 systemd 认为进程常驻,timer 逻辑会错乱。
  • ExecStart= 为要执行的命令;多命令可写多行 ExecStart=,或全写进脚本。
  • Environment= 设环境变量,比 cron 顶部 PATH 更规范。
  • User= / Group= 指定运行身份,避免任务都拿 root 权限。

8.3 .timer(何时做)

[Unit]
Description=Run my job

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=2min

[Install]
WantedBy=timers.target

(1) OnCalendar= —— 核心定时语法

格式比 cron 五段式更灵活,形如 "年-月-日 时:分:秒"

写法 含义
*:0/1 每 1 分钟
*:0/5 每 5 分钟
*-*-* 02:30:00 每天 2:30
Mon..Fri *-*-* 09:00:00 工作日上午 9 点
*-*-1..7 00:00:00 每月 1–7 号零点
daily / hourly / weekly / monthly 关键字(天/时/周/月)

技巧:

  • 可用多行 OnCalendar= 设多个闹钟,都触发同一个 service。
  • systemd-analyze calendar "写法" 可立即校验并打印今天/下次触发时刻,落盘前先测最稳。
  • 星期几写在最前面(Mon..Fri),Mon,Wed,Fri 也可。

(2) AccuracySec= —— 触发精度

timer 是省资源导向:默认 AccuracySec=1min,即在计划时刻前后 1 分钟内触发即可,让 systemd 合并多个任务减少唤醒。大多数场景够用;要准点才收窄,如 AccuracySec=1s

(3) RandomizedDelaySec= —— 随机延迟(防"惊群")

多台机器同时跑同任务(如所有服务器同时备份)会造成负载尖峰。设随机延迟错峰:

RandomizedDelaySec=2min     # 计划时刻后再随机延迟 0~2 分钟执行

(4) Persistent= —— 错过补跑(对标 anacron)

Persistent=true

计划时刻机器关机/休眠 → 下次开机立即补跑。只对 OnCalendar 生效,对相对时间定时不生效;默认 false(不补跑,错过就丢)。

(5) 相对时间触发(OnBootSec / OnUnitActiveSec …)

除了"几点"的柱状定时,还支持"相对某事件后多久":

指令 含义
OnBootSec=10min 开机后 10 分钟
OnUnitActiveSec=1h 上次 unit 开始执行后 1 小时(滚动循环最常用)
OnUnitInactiveSec=1h 上次 unit 跑完后 1 小时

"每隔 X 执行"经典写法:

[Timer]
OnUnitActiveSec=1h
OnBootSec=5min

8.4 任务重叠防护(实战重点)

cron 中若任务执行时间长于间隔,会重复拉起进程。systemd 中同一 unit 不会并发启动两次:unit 还在运行时再次触发会被跳过。给 Type=oneshot 的 service 配 timer,天然避免重叠跑批。

8.5 日志与调试(比 cron 干净太多)

journalctl -u hello.service            # 查看 job 每次执行日志
journalctl -u hello.service -f          # 实时跟随
journalctl -u hello.service --since "1 hour ago"
systemctl status hello.service          # 最近一次运行状态
systemd-analyze verify hello.service hello.timer   # 校验两个 unit
systemd-analyze calendar "*:0/1"        # 校验定时语法并预览触发时间

8.6 完整实战:每小时备份 + 随机延迟 + 错过补跑

# 1. 业务脚本
sudo tee /usr/local/bin/hourly_backup.sh > /dev/null <<'EOF'
#!/bin/bash
exec >> /var/log/hourly_backup.log 2>&1
echo "[$(date)] backup start"
tar -czf /backup/hour_$(date +%Y%m%d_%H).tar.gz -C /var www
echo "[$(date)] backup done"
EOF
sudo chmod +x /usr/local/bin/hourly_backup.sh

# 2. service(做什么)
sudo tee /etc/systemd/system/hourly_backup.service > /dev/null <<'EOF'
[Unit]
Description=Hourly website backup

[Service]
Type=oneshot
Environment="PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
ExecStart=/usr/local/bin/hourly_backup.sh
EOF

# 3. timer(何时做)
sudo tee /etc/systemd/system/hourly_backup.timer > /dev/null <<'EOF'
[Unit]
Description=Trigger hourly backup

[Timer]
OnCalendar=*-*-* *:00:00
RandomizedDelaySec=10min
Persistent=true
AccuracySec=1min

[Install]
WantedBy=timers.target
EOF

# 4. 校验 + 启动
sudo systemd-analyze verify hourly_backup.timer
sudo systemctl daemon-reload
sudo systemctl start hourly_backup.timer
sudo systemctl enable hourly_backup.timer

# 5. 查看
systemctl list-timers hourly_backup
journalctl -u hourly_backup.service

8.7 cron vs systemd timer,怎么选?

场景 推荐
简单、迁移成本低 cron
需精确日志 / 依赖控制 / 随机延迟 / 补跑 systemd timer
需秒级触发 systemd timer
旧系统 / 传统发行版 cron
新系统(RHEL 7+/Ubuntu 16.04+ 均内置) systemd timer

结论:新项目优先 systemd timer;cron 仍适合简单场景和旧系统,两者可并存。


9. 综合实战项目(推荐完整做一遍)

目标:搭建一个"网站日志每日归档 + 7 天自动清理"系统。

下面前 4 步用 cron(简单直观),第 5 步对比用 systemd timer。

# ========== 1. 模拟一个会生成日志的服务 ==========
mkdir -p ~/webapp/logs
cat > ~/webapp/run_demo.sh <<'EOF'
#!/bin/bash
echo "$(date +%F_%T) request /api from 192.168.1.$RANDOM" >> ~/webapp/logs/app.log
EOF
chmod +x ~/webapp/run_demo.sh
# 模拟每 10 秒产生一条日志(后台 1 分钟,观察即可)
(for i in $(seq 1 6); do ~/webapp/run_demo.sh; sleep 10; done) &

# ========== 2. 归档脚本 ==========
cat > ~/webapp/archive.sh <<'EOF'
#!/bin/bash
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
D=$(date +%F)
mkdir -p ~/webapp/archive
if [ -s ~/webapp/logs/app.log ]; then
  mv ~/webapp/logs/app.log ~/webapp/archive/app_${D}.log
  echo "[$(date)] archived to app_${D}.log" >> ~/webapp/archive.log
fi
# 删除 7 天前的归档
find ~/webapp/archive -name "app_*.log" -mtime +7 -delete
EOF
chmod +x ~/webapp/archive.sh

# ========== 3. 手动验证脚本 ==========
~/webapp/archive.sh && ls -l ~/webapp/archive/ && cat ~/webapp/archive.log

# ========== 4. 加入 cron:每天 23:59 归档 ==========
(crontab -l 2>/dev/null; echo "59 23 * * * $HOME/webapp/archive.sh") | crontab -
crontab -l | grep archive

# ========== 5. 对比:用 systemd timer 做同样的每日归档 ==========
sudo tee /etc/systemd/system/archive.service > /dev/null <<'EOF'
[Unit]
Description=Daily web log archive

[Service]
Type=oneshot
ExecStart=/home/$USER/webapp/archive.sh
EOF
sudo tee /etc/systemd/system/archive.timer > /dev/null <<'EOF'
[Unit]
Description=Archive daily at 23:59

[Timer]
OnCalendar=*-*-* 23:59:00
Persistent=true
RandomizedDelaySec=60

[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl start archive.timer
systemctl list-timers | grep archive

做完后你应该能回答:

  • cron 与 systemd timer 各自优缺点是什么?
  • 归档脚本为什么要"先手动跑通再接调度"?
  • 如果某天服务器关机了,这个每日归档会丢失还是会补跑?(提示:Persistent=true)

10. 排错手册(照单排查)

现象 排查方向
任务完全没执行 ① crond 在跑吗:systemctl status crond;② 看日志:grep CRON /var/log/syslogcat /var/log/cronjournalctl -u crond
执行了但没效果 先手动跑脚本看报错;查 PATH、绝对路径、权限 chmod +x
只有部分命令生效 多行命令建议写进 .sh 再调用
中文乱码/时区不对 cron 用服务器本地时区;date 看时间,必要时设 TZ 环境变量
邮件堆积 cron 默认把输出发邮件(MAILTO 可改);无邮件服务会堆积在 /var/spool/mail
脚本在错误环境执行 SHELL=/bin/bashPATH=... 写在 crontab 顶部
想立刻看到执行效果 * * * * *(每分钟)测试,而不是等天/周

最高效的调试步骤(记住这 4 步):

  1. 手动执行脚本,确认无报错;
  2. * * * * * 让任务每分钟跑一次;
  3. 命令输出重定向到日志 >> /tmp/xxx.log 2>&1
  4. 等 60 秒后查看日志和系统 cron 日志。

11. 自测题(检验掌握程度)

选择题

  1. crontab 五段式 * * * * * 依次代表什么?
    • A. 分 时 日 月 周
    • B. 时 分 日 月 周
    • C. 分 时 周 月 日
  2. */10 * * * * 表示?
    • A. 每小时第 10 分钟
    • B. 每 10 分钟
    • C. 每天 10 点
  3. cron 里"日"和"周"字段的关系是?
    • A. 且(必须同时满足)
    • B. 或(任一满足即执行)
  4. at 和 cron 的区别是?
    • A. at 周期性,cron 一次性
    • B. cron 周期性,at 一次性
    • C. 无区别

实操题

  1. 写一行 crontab:每周五下午 18:00 运行 /opt/backup.sh
  2. 让系统每天 00:30 清理 /tmp 下 3 天前的 *.tmp 文件(需写脚本并调度)。
  3. 用 at 安排"10 分钟后重启一个叫 demo 的服务"。

(答案在本指南末尾)


12. 速查卡片

# ---------- crontab ----------
crontab -l | -e | -r | -u user
# 格式: 分 时 日 月 周  命令
0 6 * * * /path/script.sh            # 每天 6 点
*/5 * * * * /path/x.sh               # 每 5 分钟
0 9-17 * * 1-5 /path/x.sh            # 工作日 9-17 点整点
PATH=/usr/local/bin:/usr/bin:/bin    # 顶部设置 PATH

# ---------- at ----------
echo "cmd" | at now + 1 hour
echo "cmd" | at 09:00 tomorrow
atq    # 查看
atrm <id>   # 删除

# ---------- systemd timer ----------
sudo systemctl start x.timer
sudo systemctl enable x.timer
systemctl list-timers
# 文件: /etc/systemd/system/x.{service,timer}

13. 自测题答案

  1. A

  2. B

  3. B(日/周是"或")

  4. B

  5. 0 18 * * 5 /opt/backup.sh(注意周字段:5 = 周五)

  6. 示例:

    # /home/user/clean_tmp.sh
    #!/bin/bash
    find /tmp -name '*.tmp' -mtime +3 -delete
    # crontab 行
    30 0 * * * /home/user/clean_tmp.sh
    
  7. echo "systemctl restart demo" | at now + 10 minutes


14. 下一步学习建议

  • ✅ 已掌握 cron 五段式、at、anacron 概念、systemd timer 基础 → 你已经能胜任大多数日常定时任务。
  • 🔍 进阶方向:cron 日志分析、run-partsflock 防止任务重叠、RandomizedDelaySec 在生产集群的应用、用 Ansible 批量下发 crontab。
  • 🧪 安全注意:不要给不信任的脚本加 crontab;涉及删除的命令务必先 find ... -print 预览再 -delete

核心心法:先手动跑通,再交给调度;永远把输出写进日志;用最短周期(每分钟)验证。

Read more

Linux 运维文件写入操作

Linux 运维文件写入操作

面向 Linux 运维/SRE 的文件写入操作参考手册。既讲每条命令怎么用,也讲它背后的机制(为什么这么行为),配真实输出示例与边界情况,最后给出常见陷阱速查表与标准动作模板。 目录 1. 总览:运维"写文件"的全景 2. Shell 重定向:机制与基础符号 3. 重定向顺序的坑:为什么 2>&1 > f 不行 4. 文件描述符与 exec:持久打开、交换、关闭 5. 防止误覆盖:noclobber 与 >| 6. Here 文档与 Here 字符串 7. tee:

By Admin
自定义你的DSH

自定义你的DSH

DSH 界面主题定制插件 给 DSH 换一套界面主题 把设计 Token 变成所见即所得的「DIY 主题」设置面板。配色、字体、背景、阴影、圆角,改完立即生效,不用写一行代码。 GitHub 仓库 更多内容 DeepSeek Harness(DSH)是 DeepSeek 推出的 AI 编程助手框架。通过 dsh web 启动浏览器界面,就能和 AI 对话、读写文件、执行命令、规划任务。功能强大,但外观一直比较朴素,默认只有浅色、深色、跟随系统三档。 我写了一个插件 dsh-ui-customizer,把界面定制做成了所见即所得的「主题设置面板」:进入 设置

By Admin
狩夜人笔记app

狩夜人笔记app

本地优先 · 离线可用 · 桌面笔记工具 狩夜人笔记 轻量 · 专注 · 个人笔记管理工具 把零散的知识,整理成自己的体系。用「主板块 / 子板块 / 文档」三级结构组织笔记,支持富文本与代码混排、语法高亮、标签、全文搜索、知识图谱与版本快照。数据保存在本地,不依赖云端。 下载安装 查看源码 核心功能 01 三级知识组织 主板块 / 子板块 / 文档,支持置顶、星标、标签与拖拽排序。 02 富文本与代码混排 TipTap 富文本编辑器,原生支持 Markdown;代码块按语言高亮、可随时切换。 03 标签与全文搜索 标签自定义颜色、侧边栏筛选;标题与正文全文检索,标签命中加权。 04 知识图谱 可视化文档之间的关联关系,直观发现知识之间的连接。

By Admin