Cron 只是偶爾發送關於輸出和錯誤的電子郵件
在 Debian 8.1 上,我使用Bash功能來檢測是否可以訪問 stackoverflow.com 網站:
(迴聲**>/dev/tcp/stackoverflow.com/80** ) &>/dev/null || echo "stackoverflow 無法訪問"
sh
這是 Bash 特有的,在. 的預設 shell 中不起作用cron
。如果我們故意嘗試 中的腳本
sh
,我們會得到:
$ /bin/sh: 1: cannot create /dev/tcp/stackoverflow.com/80: Directory nonexistent
因此,如果我只將以下內容放在我的個人 crontab 中(不設置
SHELL
為/bin/bash
) viacrontab -e
,我希望每分鐘執行一次腳本,因此我也希望每分鐘每郵件發送一次上述錯誤:* * * * * (echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow 無法訪問"
事實上,正如預期的那樣,我們可以看到
/var/log/syslog
該條目每分鐘執行一次:# **sudo grep stackoverflow /var/log/syslog** 8 月 24 日**18:58:01** localhost CRON[13719]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null ||迴聲“堆棧溢出無法訪問”) 8 月 24 日**18:59:01** localhost CRON[13723]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable") 8 月 24 日**19:00:01** localhost CRON[13727]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable") ...
在過去的約 2 小時內,這已經執行了 120 多次,因為我可以通過管道將輸出傳輸到
wc -l
.但是,從這些 >120 次的 shell 命令(重複一遍:shell 命令對 無效
/bin/sh
)中,我只收到了三封電子郵件:第一個在 19:10:01,第二個在 20:15:01,第三個在 20:57:01。
所有三封郵件的內容都完全按照預期讀取,並且完全包含在不兼容的 shell 中執行腳本時預期的錯誤消息(故意)。例如,我收到的第二封郵件是這樣寫的(另外兩封幾乎相同):
來自 mat@myhost.com 2015 年 8 月 24 日星期一 20:15:01 來自:root@myhost.com(Cron 守護程序) 至:mat@myhost.com 主題:Cron (echo >/dev/tcp/stackoverflow.com/80)&>/dev/null || echo "stackoverflow 無法訪問" ... **/bin/sh: 1: 無法創建 /dev/tcp/stackoverflow.com/80: 目錄不存在`**
從
/var/log/mail.log
,我看到這三封郵件是過去幾個小時內發送和接收的唯一郵件。因此,由於錯誤腳本創建的上述輸出,我們希望從 cron 收到的超過 100 封額外郵件在哪裡?
總結一下:
- 此系統上的郵件配置正確,我可以毫無問題地發送和接收郵件
/usr/bin/sendmail
。- Cron 設置正確,按預期注意到任務並在配置的時間精確地執行它。我已經嘗試了許多其他任務和調度選項,並且 cron 完全按照預期執行了它們。
- 腳本總是寫輸出(見下文),因此我們希望 cron 每次呼叫都會通過郵件將輸出發送給我。
- 輸出只是偶爾郵寄給我,在大多數情況下顯然被忽略了。
有很多方法可以解決導致上述觀察的明顯錯誤:
- 我可以
SHELL=/bin/bash
在我的crontab
.- 我可以創建一個
heartbeat.sh
with#!/bin/bash
並呼叫它。/bin/bash -c ...
我可以使用inside 呼叫腳本crontab
。- 等等,所有這些都修復了在
sh
.然而,所有這些都沒有解決這個問題的核心問題,即在這種情況下,
cron
即使腳本總是創建輸出,也不能可靠地發送郵件。我已經驗證腳本總是通過創建來創建輸出
wrong.sh
(再次故意使用不合適的/bin/sh
外殼,以產生cron
應該看到的相同錯誤):**#!/bin/sh** (迴聲 >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow 無法訪問"
現在我可以在循環中呼叫腳本,看看是否有過在沒有創建輸出的情況下完成的情況。使用重擊:
$雖然是真的;做 [[ -n $(./wrong.sh 2>&1 ) ]]; 迴聲$?;完成 | grep -v 0
即使在數千次呼叫中,我也無法重現腳本在沒有創建輸出的情況下完成的情況。
這種不可預測的行為可能是什麼原因造成的?任何人都可以重現這個嗎?對我來說,看起來可能存在 cron 可能會錯過腳本輸出的競爭條件,可能主要涉及錯誤源於 shell 本身的情況。謝謝!
經過進一步測試,我懷疑這
&
會影響您的結果。正如您所指出的,&>/dev/null
是bash語法,而不是sh語法。因此,sh
正在創建一個子外殼並將其作為背景。當然,子shellecho
會創建stderr,但我的理論是:
- cron 沒有擷取 subshell 的 stderr,並且
- 子shell 的後台處理總是成功完成,從而繞過你的
|| echo ...
.…導致 cron 作業沒有輸出,因此沒有郵件。根據我對 vixie-cron 原始碼的閱讀,似乎作業的 stderr 和 stdout 會被 cron 擷取,但它一定會被 subshell 失去。
在 /bin/sh 環境中自己測試它(假設您在這裡沒有名為 ‘bar’ 的文件):
(grep foo bar) & echo $?