參考資料
- 【図解】cronの仕組み
- 定時任務 - crontab
- 解決ubuntu下定時任務不執行問題
- crontab環境變量問題💥
- Linux定時任務功能詳解:crontab與at命令應用指南
目錄
- 一. 環境準備
- 1.1 wsl開啟systemd
- 1.2 開啟cron日志
- 二. cron服務管理相關命令
- 2.1 service 的方式
- 2.2 systemctl 的方式
- 三. 簡單使用crontab定時任務
- 3.1 編輯一個定時任務
- 3.1.1 `crontab -e` 編輯定時任務
- 3.1.2 通過命令行直接添加
- 3.2 查看定時任務
- 3.2.1 `crontab -l` 查看既存的定時任務
- 3.2.2 `/var/spool/cron/crontabs/用戶名` 文件查看
- 3.3 刪除定時任務
- 3.3.1 `crontab -e` 編輯刪除定時任務
- 3.3.2 按關鍵字在命令行刪除
- 3.3.3 `crontab -r` 的方式刪除
- 四. 定時任務進階
- 4.1 注意事項
- 4.2 定時任務的示例
- 4.2.1 由于PATH引起問題的實例
- 4.2.2 其他定時任務示例
一. 環境準備
1.1 wsl開啟systemd
?筆者使用wsl來運行ubuntu,而 wsl
默認沒有真正的 init
系統,無法使用現代 Linux 的標準服務管理工具:systemctl
命令。
可通過修改/etc/wsl.conf
的方式開啟使用systemctl
命令。如果使用的是VMware等虛擬機或者云服務器則默認會開啟systemctl
命令。
apluser@FengYeHong-HP:~$ ls -l /etc/wsl.conf
-rw-r--r-- 1 root root 43 Aug 14 20:50 /etc/wsl.conf
apluser@FengYeHong-HP:~$
apluser@FengYeHong-HP:~$ tail -n 2 /etc/wsl.conf
[boot]
systemd=true
?修改完畢/etc/wsl.conf
之后,退出wsl,在PowerShell
終端中執行關閉wsl的命令
wsl --shutdown
?然后再一次進入wsl之后,systemd init 系統
就已經被啟用了
- 可通過下面的命令進行驗證
apluser@FengYeHong-HP:~$ ps -p 1 -o comm=
systemd
💥注意事項💥
- wsl1無法啟用
systemd init 系統
,wsl2才可以。 - 啟用
systemd
后,WSL 啟動會比以前慢一些,因為它會啟動一堆后臺服務(dbus、journald、cron 等)
1.2 開啟cron日志
?ubuntu默認沒有開cron
日志,可通過修改配置文件選擇是否開啟。去除#
既可以開啟cron
日志。
apluser@FengYeHong-HP:~$ ls -l /etc/rsyslog.d/50-default.conf
-rw-r--r-- 1 root root 1123 Aug 13 09:00 /etc/rsyslog.d/50-default.conf
apluser@FengYeHong-HP:~$
apluser@FengYeHong-HP:~$ grep cron /etc/rsyslog.d/50-default.conf
cron.* /var/log/cron.log
# cron,daemon.none;\
?配置文件修改完畢之后,還需要重啟一下rsyslog
服務,當定時任務開始執行后,就可以在/var/log/cron.log
中看到日志了。
# 這兩種方式都可以
sudo systemctl restart rsyslog
sudo service rsyslog restart
二. cron服務管理相關命令
特性 | service | systemctl |
---|---|---|
誕生時代 | SysV init 時代(上世紀 90 年代) | systemd 時代(2010 年以后) |
依賴的 init 系統 | SysV init / Upstart / systemd(兼容模式) | 只能用于 systemd |
底層調用 | 在舊系統調用 /etc/init.d/xxx 腳本,在新系統里會轉到 systemctl | 直接通過 systemd 的 D-Bus API 管理服務 |
功能范圍 | 主要是啟動、停止、重啟、查看狀態 | 除了服務管理,還能管理開機啟動、目標(target)、掛載點、設備、電源等 |
輸出信息 | 簡單的狀態行(舊模式),或 systemd 的詳細狀態(新模式) | 詳細的 systemd 狀態、日志、依賴關系 |
是否未來主流 | 只是為了兼容舊腳本保留 | 現代 Linux 的標準,未來主流 |
2.1 service 的方式
# 查看crontab服務狀態
ps -ef | grep '[c]ron'
service cron status sudo service cron start # 啟動服務
sudo service cron stop # 關閉服務
sudo service cron restart # 重啟服務
sudo service cron reload # 重新載入配置
2.2 systemctl 的方式
# 查看crontab服務狀態
ps -ef | grep '[c]ron'
systemctl status cronsudo systemctl start cron # 啟動
sudo systemctl stop cron # 停止
sudo systemctl restart cron # 重啟
sudo systemctl enable cron # 開機自啟
sudo systemctl disable cron # 禁用開機自啟
三. 簡單使用crontab定時任務
3.1 編輯一個定時任務
3.1.1 crontab -e
編輯定時任務
- 直接在命令行輸入
crontab -e
編輯定時任務
apluser@FengYeHong-HP:~$ crontab -e
- 輸入完成之后,會使用指定的文本編輯器(此處使用的是nano編輯器)打開定時任務編輯頁面
3.1.2 通過命令行直接添加
?如果定時任務的命令長度不是十分長的話,可以直接通過命令行追加
crontab -l 2>/dev/null
:讀取現有任務,如果沒有就忽略錯誤echo '...'
:指定定時任務的內容crontab -
:覆蓋回 crontab,添加定時任務
(crontab -l 2>/dev/null; echo '* * * * * echo "Hello world" >> /tmp/hello.log') | crontab -
3.2 查看定時任務
3.2.1 crontab -l
查看既存的定時任務
apluser@FengYeHong-HP:~$ crontab -l
* * * * * echo "Hello world" >> /tmp/hello.log
3.2.2 /var/spool/cron/crontabs/用戶名
文件查看
- 每個用戶對應的定時任務都會保存到
/var/spool/cron/crontabs/用戶名
文件中
apluser@FengYeHong-HP:~$ sudo ls -l /var/spool/cron/crontabs/apluser
-rw------- 1 apluser crontab 222 Aug 13 08:52 /var/spool/cron/crontabs/apluser
apluser@FengYeHong-HP:~$ sudo cat /var/spool/cron/crontabs/apluser
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (- installed on Wed Aug 13 08:52:05 2025)
# (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)
* * * * * echo "Hello world" >> /tmp/hello.log
3.3 刪除定時任務
3.3.1 crontab -e
編輯刪除定時任務
crontab -e
3.3.2 按關鍵字在命令行刪除
# 可以先將既存的定時任務備份
crontab -l > mycron.bk
# 然后通過關鍵詞的方式刪除定時任務
crontab -l | grep -v 'Hello world'
crontab -l | grep -v 'Hello world' | crontab -
3.3.3 crontab -r
的方式刪除
- 💥注意:此種方式會刪除當前用戶的所有定時任務,慎用。
apluser@FengYeHong-HP:~$ crontab -ir
crontab: really delete apluser crontab? (y/n) y
apluser@FengYeHong-HP:~$
apluser@FengYeHong-HP:~$ crontab -l
no crontab for apluser
apluser@FengYeHong-HP:~$ sudo ls -l /var/spool/cron/crontabs/apluser
[sudo] password for apluser:
ls: cannot access '/var/spool/cron/crontabs/apluser': No such file or directory
四. 定時任務進階
?/etc/crontab
是 系統級別的 Cron
配置文件
- 設置一些 系統維護任務(清理日志、同步時間等)
- 啟動時執行系統服務相關腳本
- 運行需要 root 權限的定時任務
apluser@FengYeHong-HP:~$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.SHELL=/bin/sh
# You can also override PATH, but by default, newer versions inherit it from the environment
#PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin# 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
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
4.1 注意事項
- ?
PATH
變量被精簡- 🔷在交互式 shell 里,PATH 可能很長,例如
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:...
- 🔷但
cron
里常常只有
/usr/bin:/bin
- ?
HOME
變量可能不同- 交互 shell 下的
HOME
一般是當前用戶的家目錄,但 cron 可能會是/
或其他值。 - 如果腳本依賴
~
或相對路徑
,建議使用絕對路徑。
- 交互 shell 下的
- ?不會加載
~/.bashrc
或~/.profile
- cron 不會讀取你時登錄時加載的配置文件,所以環境變量、別名、函數都可能丟失。
- 如果腳本依賴這些配置,可以在任務里手動加載
- ?
SHELL
變量可能不同- cron 默認使用
/bin/sh
(在很多系統上是dash
),而不是/bin/bash
。 - 如果腳本里用了
bash
特性([[ ]]
、{1..5}
、source
等),除了在腳本內指定Shebang
之外,還可以再定時任務編輯界面添加
SHELL=/bin/bash
- cron 默認使用
- ?
%
的特殊含義- 在 crontab 中
%
,表示換行(相當于把它后面的內容當作命令的標準輸入STDIN
)。 - 如果要在命令里用
%
,必須寫成\%
才能被當作普通字符。 -
# 在終端執行會輸出 Hello%World # 但是放進 crontab 里會變成 # 命令:echo "Hello" # 標準輸入:World * * * * * echo "Hello%World"
- 在 crontab 中
4.2 定時任務的示例
4.2.1 由于PATH引起問題的實例
PATH
變量在交互式shell中,會輸出很多內容
apluser@FengYeHong-HP:~$ echo "$PATH" | xargs -d ':' -L 1 | grep -v '威' | wc -l
48
apluser@FengYeHong-HP:~$ echo "$PATH" | xargs -d ':' -L 1 | grep -v '威' | head
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/usr/lib/wsl/lib
/mnt/c/Program Files (x86)/VMware/VMware Workstation/bin/
- 但我們在corn定時任務中使用了
PATH
之后,只會輸出很少的內容
apluser@FengYeHong-HP:~$ crontab -l
SHELL=/bin/bash
* * * * * /home/apluser/work/0815/bash_test.shapluser@FengYeHong-HP:~$ cat /home/apluser/work/0815/bash_test.sh
#!/usr/bin/env bash
echo "$PATH" | xargs -d ':' -L 1 | grep -v '威' >> /home/apluser/work/corn1.logapluser@FengYeHong-HP:~$ tail /home/apluser/work/corn1.log
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/binapluser@FengYeHong-HP:~$
4.2.2 其他定時任務示例
# 實例1:每1分鐘執行一次myCommand
* * * * * myCommand# 實例2:每小時的第3和第15分鐘執行
3,15 * * * * myCommand# 實例3:在上午8點到11點的第3和第15分鐘執行
3,15 8-11 * * * myCommand# 實例4:每隔兩天的上午8點到11點的第3和第15分鐘執行
3,15 8-11 */2 * * myCommand# 實例5:每周一上午8點到11點的第3和第15分鐘執行
3,15 8-11 * * 1 myCommand# 實例6:每晚的21:30重啟smb
30 21 * * * /etc/init.d/smb restart# 實例7:每月1、10、22日的4 : 45重啟smb
45 4 1,10,22 * * /etc/init.d/smb restart# 實例8:每周六、周日的1 : 10重啟smb
10 1 * * 6,0 /etc/init.d/smb restart# 實例9:每天18 : 00至23 : 00之間每隔30分鐘重啟smb
0,30 18-23 * * * /etc/init.d/smb restart# 實例10:每星期六的晚上11 : 00 pm重啟smb
0 23 * * 6 /etc/init.d/smb restart# 實例11:每一小時重啟smb
* */1 * * * /etc/init.d/smb restart# 實例12:晚上11點到早上7點之間,每隔一小時重啟smb
0 23-7 * * * /etc/init.d/smb restart
?在 crontab
里,@reboot
是一種特殊的時間標記,表示系統啟動完成后執行一次。
- 當系統啟動(或 cron 服務啟動)后,運行一次 script_a.py
>> /home/pi/cron.log 2>&1
:把標準輸出和錯誤輸出都追加寫入/home/pi/cron.log
@reboot /usr/bin/python3 /home/pi/script_a.py >> /home/pi/cron.log 2>&1
?常見的特殊時間標記還有
語法 | 含義 |
---|---|
@reboot | 系統啟動后運行一次 |
@yearly | 每年運行一次(等同于 0 0 1 1 * ) |
@monthly | 每月運行一次(等同于 0 0 1 * * ) |
@weekly | 每周運行一次(等同于 0 0 * * 0 ) |
@daily | 每天運行一次(等同于 0 0 * * * ) |
@hourly | 每小時運行一次(等同于 0 * * * * ) |