Cron 只是偶尔发送关于输出和错误的电子邮件

mat*_*mat 2 linux shell bash cron debian

在 Debian 8.1 上,我使用Bash功能来检测是否可以访问 stackoverflow.com 网站:

(echo >/dev/tcp/stackoverflow.com/80 ) &>/dev/null || 回声“无法访问stackoverflow”

这是 Bash 特定的sh,在cron.

如果我们故意尝试 中的脚本sh,我们会得到:

$ /bin/sh: 1: cannot create /dev/tcp/stackoverflow.com/80: Directory nonexistent

因此,如果我只将以下内容放在我的个人 crontab 中(没有设置SHELL/bin/bash) via crontab -e,我希望每分钟执行一次脚本,因此我希望每分钟每封邮件发送一次上述错误:

* * * * * (echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || 回声“无法访问stackoverflow”

事实上,正如预期的那样,我们从/var/log/syslog条目中看到每分钟执行一次:

# sudo grep stackoverflow /var/log/syslog 
Aug 24 18:58:01 localhost CRON[13719]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null ||回声“无法访问stackoverflow”)
8 月 24 日18:59:01本地主机 CRON[13723]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable")
8 月 24 日19:00:01本地主机 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 Daemon)
至:mat@myhost.com
主题:Cron (echo >/dev/tcp/stackoverflow.com/80)&>/dev/null || 回声“无法访问stackoverflow”
...

/bin/sh: 1: 无法创建 /dev/tcp/stackoverflow.com/80: 目录不存在`

/var/log/mail.log,我看到这三封邮件是过去几个小时内发送和接收的唯一邮件。

因此,由于错误脚本创建的上述输出,我们希望从 cron 收到的 >100 封额外邮件在哪里?

总结一下:

  1. 邮件在这个系统上配置正确,我可以毫无问题地发送和接收邮件/usr/bin/sendmail
  2. Cron 设置正确,按预期通知任务并在配置的时间精确执行。我尝试了许多其他任务和调度选项,cron 完全按照预期执行了它们。
  3. 该脚本总是写入输出(见下文),因此我们希望 cron 每次调用都会通过邮件将输出发送给我。
  4. 输出只是偶尔邮寄给我,在大多数情况下显然被忽略了。

有很多方法可以解决导致上述观察结果的明显错误:

  1. 我可以SHELL=/bin/bash在我的crontab.
  2. 我可以创建一个heartbeat.shwith #!/bin/bash,然后调用它。
  3. 我可以调用脚本/bin/bash -c ...crontab
  4. 等等,所有这些都修复了在sh.

然而,所有这些都没有解决这个问题的核心问题,即在这种情况下,cron即使脚本总是创建输出,也不能可靠地发送邮件。

我已经验证脚本总是通过创建来创建输出wrong.sh(它再次故意使用不合适的/bin/sh外壳,以产生cron应该看到的相同错误):

#!/bin/sh
(echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || 回声“无法访问stackoverflow”

现在我可以在循环中调用脚本,看看是否存在它在创建输出的情况下完成的情况。使用Bash

$ 为真;做 [[ -n $(./wrong.sh 2>&1 ) ]]; 回声 $?; 完成 | grep -v 0

即使在数千次调用中,我也无法重现脚本完成而不创建输出的情况。

这种不可预测的行为可能是什么原因?任何人都可以重现这个吗?对我来说,看起来可能存在竞争条件,cron 可能会错过脚本的输出,可能主要涉及错误源于 shell 本身的情况。谢谢!

Jef*_*ler 5

经过进一步测试,我怀疑这&会影响您的结果。正如您所指出的,&>/dev/nullbash语法,而不是sh语法。因此,sh正在创建一个子外壳并将其作为背景。当然,子外壳echo会创建 stderr,但我的理论是:

  1. cron 没有捕获 subshel​​l 的 stderr,并且
  2. 子shell的背景总是成功完成,从而绕过你的|| echo ....

...导致 cron 作业没有输出,因此没有邮件。根据我对 vixie-cron 源代码的阅读,似乎作业的 stderr 和 stdout 会被 cron 捕获,但它肯定会被 subshel​​l 丢失。

在 /bin/sh 环境中自行测试(假设您这里没有名为“bar”的文件):

(grep foo bar) &
echo $?
Run Code Online (Sandbox Code Playgroud)

  • 我认为答案与您所说的很接近,但实际上是 subshel​​l 和 `cron` 之间的竞争。由于子 shell 是后台的,`cron` 会立即停止侦听它。但偶尔它会设法在 `cron` 仍在监听时非常快速地挤出错误消息。 (2认同)