Cron (正體中文) - ArchWiki

Jump to content

# Cron (正體中文)

From ArchWiki
翻譯狀態：本文章是 Cron 的翻譯版本。最近一次的翻譯時間：2021-04-20。點擊本連結查看英文頁面之後的變更。

本條目或段落需要進行翻譯。

註記: Translation is not complete 需要翻譯，尚未完成 (討論)

相關文章

- systemd/Timers

摘錄維基百科:

  cron 是一款類Unix的作業系統下的基於時間的任務管理系統。用戶們可以通過cron在固定時間、日期、間隔下，運行定期任務（可以是命令和腳本）。cron常用於運維和管理，但也可用於其他地方，如：定期下載文件和郵件。

## 安裝

有很多 cron 的實做方法，但都不是預設安裝的，因為基本系統使用的是 systemd/Timers 。 參見 Gentoo 的 cron 指南，它提供了一些比較。

可供使用的套件：

- cronie
- fcron
- dcronAUR
- vixie-cronAUR
- scron-gitAUR

## 配置

### 啟動和自動啟動

安裝後，守護進程預設不會啟動。安裝的套件包很可能提供一種透過 systemctl 來控制的服務。 例如： cronie 使用 `cronie.service` 。

檢查 `/etc/cron.daily/` 和相似的目錄，看看有哪些工作存在。啟動 cron 服務會觸發所有的工作。

註記: cronie 提供 `0anacron` 隨時 工作，允許 延遲其他工作 例如：在標準執行時電腦被關機。

### 工作的除錯

cron 會紀錄來自 stdout 和 stderr 的輸出，並試著透過 `sendmail` 指令將其作為電子郵件發送到使用者的 spool 。如果沒有找到 `/usr/bin/sendmail` ， Cronie 將會禁用郵件輸出。為了讓郵件被寫入使用者的 spool ，系統中必須有一個 smtp 的守護進程，例如： opensmtpd 。除此之外你還可以安裝一個提供 sendmail 命令的套件包，並配置它將郵件發送到遠端的郵件交換器。你也可以透過使用 `-m` 選項並寫一個自定義的腳本來紀錄郵件。

提示： 可以使用 Postfix#Local mail 發送輸出給本機使用者。

1. 修改 `cronie.service` 單元。
2. 安裝 esmtpAUR, msmtp, opensmtpd, sSMTP，或寫一個自定義的腳本。

#### sSMTP 的範例

sSMTP 是一個只發送郵件的模擬器，它可以將郵件從本機發送到 smtp 伺服器上。雖然目前還沒有活躍的維護者，但它仍然是迄今為止將郵件傳輸到配置好的郵件中心最簡單的方法。它不需要執行守護進程，只需在一個配置文件中編輯三行即可（如果你的主機可以透過 mailhub 轉未經認證的郵件）。

安裝 ssmtpAUR，它會從 `/usr/bin/sendmail` 建立一個軟連結到 `/usr/bin/ssmtp`。然後你必須編輯 `/etc/ssmtp/ssmtp.conf`。詳情請參閱 sSMTP。建立一個軟連結到 `/usr/bin/sendmail` 可以確保像 S-nail （或任何提供`/usr/bin/mail` 的程式）都可以不需修改便能正常工作。

重啟 `cronie` ，以確保它偵測到你現在已經安裝了 `/usr/bin/sendmail` 。

#### msmtp 的範例

安裝 msmtp-mta，它會建立一個指向 `/usr/bin/msmtp` 而位於 `/usr/bin/sendmail` 的符號連結。重新啟動 `cronie` 以確保它能夠辨識新的 `sendmail` 命令。你需要給 `msmtp` 一個媒界，方可把你的用戶轉成電郵地址。

然後把 `MAILTO` 加到你的 crontab﹕

    MAILTO=your@email.com

或者 建立 `/etc/msmtprc` 再加上：

    aliases /etc/aliases

然後建立 `/etc/aliases`：

    your_username: your@email.com
    # Optional:
    default: your@email.com

接着 更改 cronie 守護行程的設定。你可以把 `ExecStart` 命令更改為：

    ExecStart=/usr/bin/crond -n -m '/usr/bin/msmtp -t'

#### esmtp 的範例

安裝 esmtpAUR 和 procmailAUR。

安裝後可設定路由如下：

    /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 需要超級使用者權限方可於 delivery mode 中執行，但如果你會以超級使用者的身份來執行 cronjobs，這並不會引起問題。

建立一個名為 `message.txt` ，內容為 `"test message"` 的檔案，以便測試，確認所有東西都正常運作。

在同一個目錄裡執行：

    $ sendmail user_name < message.txt

以及：

    $ cat /var/spool/mail/user_name

你應該能夠看見 `"test message"` 與發送日期及時間。

所有錯誤訊息將會被重新導向到 `/var/spool/mail/user_name`。

權限的問題令到建立及傳送郵件到超級使用者變得非常困難。（例﹕`su -c ""`）你可以如下要求 `esmtp` 去發送超級使用者全部的郵件到一個平凡的用家﹕

    /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

用家們 可於任何能夠處理 mbox 格式的 讀者 去檢查他的郵件，或者看一看檔案 `/var/spool/mail/user`。如果所有東西都在預期之內，你可以為將來開機而 啟用 opensmtpd 單元。

這可以避免把局部性的 cron 通知發送到一個遠端侍服器。可惜，這個方法需要多一個守護行程。

註記:

- 於撰寫此頁之時，Arch 的 opensmtpd 套件不會於 `/var/spool/smtpd` 建立全部所需的目錄，但是其守護行程將會警告關於所需的擁有者和權限。你可根據那個警告的建議去建立它們。
- 即使那個建議的設定不會允許遠端接通，用 iptables 或類似的程式去阻止端口 25 以加多一層安全性還是一個好的保險操施。

#### Long cron job

Suppose this program is invoked by cron :

    #!/bin/sh
    echo "I had a recoverable error!"
    sleep 1h

What happens is this:

1. cron runs the script
2. as soon as cron sees some output, it runs your MTA, and provides it with the headers. It leaves the pipe open, because the job has not finished and there might be more output.
3. the MTA opens the connection to postfix and leaves that connection open while it waits for the rest of the body.
4. 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'

To solve this problem you can use the command chronic or sponge from moreutils. From their respective man page:

chronic
  chronic runs a command, and arranges for its standard out and standard error to only be displayed if the command fails (exits nonzero or crashes). If the command succeeds, any extraneous output will be hidden.
sponge
  sponge reads standard input and writes it out to the specified file. Unlike a shell redirect, sponge soaks up all its input before opening the output file… If no output file is specified, sponge outputs to stdout.

Chronic too buffers the command output before opening its standard output.

## Crontab format

The basic format for a crontab is:

    minute hour day_of_month month day_of_week command

- minute values can be from 0 to 59.
- hour values can be from 0 to 23.
- day_of_month values can be from 1 to 31.
- month values can be from 1 to 12.
- day_of_week values can be from 0 to 6, with 0 denoting Sunday.

Spaces are used to separate fields. To fine-tune your schedule you may also use one of the following symbols:

Symbol | Description
--- | ---
* | Wildcard, specifies every possible time interval
, | List multiple values separated by a comma.
- | Specify a range between two numbers, separated by a hyphen
/ | Specify a periodicity/frequency using a slash

For example, the line:

    */5 9-16 * 1-5,9-12 1-5 ~/bin/i_love_cron.sh

will execute the script `i_love_cron.sh` at five minute intervals from 9 AM to 4:55 PM on weekdays except during the summer months (June, July, and August).

In addition, crontab has some special keywords:

    @reboot at startup
    @yearly once a year
    @annually ( == @yearly)
    @monthly once a month
    @weekly once a week
    @daily once a day
    @midnight ( == @daily)
    @hourly once an hour

For example:

    @reboot ~/bin/i_love_cron.sh

Will execute the script `i_love_cron.sh` at startup.

See more at: https://www.adminschoice.com/crontab-quick-reference

## Basic commands

Crontabs should never be edited directly; instead, users should use the `crontab` program to work with their crontabs. To be granted access to this command, user must be a member of the users group (see the `gpasswd` command).

To view their crontabs, users should issue the command:

    $ crontab -l

To edit their crontabs, they may use:

    $ crontab -e

註記: By default the `crontab` command uses the `vi` editor. To change it, export `EDITOR` or `VISUAL`, or specify the editor directly: `EDITOR=vim crontab -e`.

To remove their crontabs, they should use:

    $ crontab -r

If a user has a saved crontab and would like to completely overwrite their old crontab, they should use:

    $ crontab saved_crontab_filename

To overwrite a crontab from the command line (Wikipedia:stdin), use

    $ crontab -

To edit somebody else's crontab, issue the following command as root:

    # crontab -u username -e

This same format (appending `-u username` to a command) works for listing and deleting crontabs as well.

## Examples

The entry:

    01 * * * * /bin/echo Hello, world!

runs the command `/bin/echo Hello, world!` on the first minute of every hour of every day of every month (i.e. at 12:01, 1:01, 2:01, etc.).

Similarly:

    */5 * * jan mon-fri /bin/echo Hello, world!

runs the same job every five minutes on weekdays during the month of January (i.e. at 12:00, 12:05, 12:10, etc.).

The line (as noted in "man 5 crontab"):

    *0,*5 9-16 * 1-5,9-12 1-5 /home/user/bin/i_love_cron.sh

will execute the script `i_love_cron.sh` at five minute intervals from 9 AM to 5 PM (excluding 5 PM itself) every weekday (Mon-Fri) of every month except during the summer (June, July, and August).

Periodical settings can also be entered as in this crontab template:

    # 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

Here are some self-explanatory crontab syntax examples:

    30 4 echo "It is now 4:30 am."
    0 22 echo "It is now 10 pm."
    30 15 25 12 echo "It is 3:30pm on Christmas Day."
    30 3 * * * echo "Remind me that it's 3:30am every day."
    0 * * * * echo "It is the start of a new hour."
    0 6 1,15 * * echo "At 6am on the 1st and 15th of every month."
    0 6 * * 2,3,5 echo "At 6am on Tuesday, Wednesday and Thursdays."
    59 23 * * 1-5 echo "Just before midnight on weekdays."
    0 */2 * * * echo "Every two hours."
    0 20 * * 4 echo "8pm on a Thursday."
    0 20 * * Thu echo "8pm on a Thursday."
    */15 9-17 * * 2-5 echo "Every 15 minutes from 9am-5pm on weekdays."
    @yearly echo "Happy New Year!"

## Default editor

To use an alternate default editor, define the `EDITOR` environment variable in a shell initialization script as described in Environment variables.

As a regular user, `su` will need to be used instead of `sudo` for the environment variable to be pulled correctly:

    $ su -c "crontab -e"

To have an alias to this `printf` is required to carry the arbitrary string because `su` launches in a new shell:

    alias scron="su -c $(printf "%q " "crontab -e")"

## Running X.org server-based applications

Cron does not run under the X.org server therefore it cannot know the environmental variable necessary to be able to start an X.org server application so they will have to be defined. One can use a program like xuserrun-gitAUR to do it:

    17 02 * ... /usr/bin/xuserrun /usr/bin/xclock

Or they can be defined manually (`echo $DISPLAY` will give the current DISPLAY value):

    17 02 * ... env DISPLAY=:0 /usr/bin/xclock

If running notify-send for desktop notifications in cron, notify-send is sending values to dbus. So it needs to tell dbus to connect to the right bus. The address can be found by examining DBUS_SESSION_BUS_ADDRESS environment variable and setting it to the same value. Therefore:

    17 02 * ... env DBUS_SESSION_BUS_ADDRESS=your-address notify-send 'Foo bar'

If done through say SSH, permission will need be given:

    # xhost +si:localuser:$(whoami)

## Asynchronous job processing

If you regularly turn off your computer but do not want to miss jobs, there are some solutions available (easiest to hardest):

### Cronie

cronie comes with anacron included. The project homepage says:

Cronie contains the standard UNIX daemon crond that runs specified programs at scheduled times and related tools. It is based on the original cron and has security and configuration enhancements like the ability to use pam and SELinux.

### Dcron

Vanilla dcronAUR supports asynchronous job processing. Just put it with @hourly, @daily, @weekly or @monthly with a jobname, like this:

    @hourly         ID=greatest_ever_job      echo This job is very useful.

### Cronwhip

cronwhipAUR is a script to automatically run missed cron jobs; it works with the former default cron implementation, dcron. See also the forum thread.

### Anacron

Anacron is a full replacement for dcron which processes jobs asynchronously.

It is provided by cronie. The configuration file is `/etc/anacrontab`. Information on the format can be found in the `anacrontab(5)` man page. Running `anacron -T` will test `/etc/anacrontab` for validity.

### Fcron

Like anacron, fcron assumes the computer is not always running and, unlike anacron, it can schedule events at intervals shorter than a single day which may be useful for systems which suspend/hibernate regularly (such as a laptop). Like cronwhip, fcron can run jobs that should have been run during the computer's downtime.

When replacing cronie with fcron be aware the spool directory is `/var/spool/fcron` and the `fcrontab` command is used instead of crontab to edit the user crontabs. These crontabs are stored in a binary format with the text version next to them as foo.orig in the spool directory. Any scripts which manually edit user crontabs may need to be adjusted due to this difference in behavior.

A quick scriptlet which may aide in converting traditional user crontabs to fcron format:

    cd /var/spool/cron && (
     for ctab in *; do
      fcrontab ${ctab} -u ${ctab}
     done
    )

See also the forum thread.

## Ensuring exclusivity

If you run potentially long-running jobs (e.g., a backup might all of a sudden run for a long time, because of many changes or a particular slow network connection), then `flock` (util-linux) can ensure that the cron job will not start a second time.

      5,35 * * * * /usr/bin/flock -n /tmp/lock.backup /root/make-backup.sh

## Cronie

The relevant file hierarchy for cronie is the following:

       /etc/
         |----- cron.d/
                  | ----- 0hourly
         |----- cron.minutely/
         |----- cron.hourly/
                  | ----- 0anacron
         |----- anacrontab
         |----- cron.daily/
         |----- cron.monthly/
         |----- cron.weekly/
         |----- crontab
         |----- cron.deny

Cronie provides both cron and anacron functionalities: cron runs jobs at regular time intervals (down to a granularity of one minute) as long as the system is available at the specified time, while anacron executes commands at intervals specified in days. Unlike cron, it does not assume that the system is running continuously. Whenever the system is up, anacron checks if there are any jobs that should have been run and handles them accordingly.

A cron job can be defined in a crontab-like file in the `/etc/cron.d` directory or added within the `/etc/crontab` file. Note the latter is not present by default but is used if it exists. As instructed by `/etc/cron.d/0hourly`, any executable file in `/etc/cron.hourly` will be run every hour (by default at minute 1 of the hour). Files in `/etc/cron.minutely` are executed every minute if instructed accordingly in `/etc/cron.d/0hourly`. The executables are typically shell scripts, symlinks to executable files can also be used. The `/etc/cron.deny` file includes the list of users not allowed to use crontab, without this file, only users listed in `/etc/cron.allow` can use it.

Anacron works similarly, by executing the files in the `/etc/cron.daily`, `/etc/cron.weekly` and `/etc/cron.monthly` directories, placed there depending on the desired job frequency. The cron job `/etc/cron.hourly/0anacron` makes sure that anacron is run once daily to perform its pending tasks.

註記:

- Cronie uses `run-parts` to carry out scripts in the different directories. The filenames should not include any dot (.) since `run-parts` in its default mode will silently ignore them (see run-parts(8)). The names must consist only of upper and lower-case letters, digits, underscores and minus-hyphens.
- The output of `systemctl status cronie` might show a message such as `CAN'T OPEN (/etc/crontab): No such file or directory`. However, this can be ignored since cronie does not require one.
- Cronie is particular about the permissions for `/etc/cron.d/0hourly`. None of the tasks in `/etc/cron.d/{hourly,weekly,daily} ... etc` will be run (including the anacron launcher) if `/etc/cron.d/0hourly` is damaged or has improper permissions. `pacman -Qkk cronie` can show if you have any such issues.

提示: To prevent the sending of output and stop email alert, add `>/dev/null 2>&1` at the end of the line for each cron job to redirect output to /dev/null.

    0 1 5 10 * /path/to/script.sh >/dev/null 2>&1

You can also set `MAILTO=””` variable in your crontab file to disable email alerts.

## Dcron

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

These lines exemplify one of the formats that crontab entries can have, namely whitespace-separated fields specifying:

1. @period
2. ID=jobname (this tag is specific to dcron)
3. command

The other standard format for crontab entries is:

1. minute
2. hour
3. day
4. month
5. day of week
6. command

The crontab files themselves are usually stored as `/var/spool/cron/username`. For example, root's crontab is found at `/var/spool/cron/root`

See the crontab man page for further information and configuration examples.

## See also

- Gentoo Linux Cron Guide
- crontab.guru - online editor for cronjob expressions

Category:

- System administration (正體中文)

Hidden category:

- Pages or sections flagged with Template:Translateme (正體中文)