【问题标题】:Can a bash script tell if it's being run via cron?bash 脚本能否判断它是否通过 cron 运行?
【发布时间】:2011-03-13 23:56:16
【问题描述】:

在谷歌上搜索这个问题时运气不佳,我曾考虑将其发布在 SF 上,但它实际上似乎是一个开发问题。如果没有,请随时迁移。

所以,我有一个脚本,每天早上 3 点左右通过 cron 运行。我有时也手动运行相同的脚本。问题是每次我手动运行我的脚本并且它失败时,它都会向我发送一封电子邮件;即使我可以查看输出并在控制台中查看错误。

有没有办法让 bash 脚本告诉它正在通过 cron 运行(也许通过使用 whoami)并且只有在这样的情况下才发送电子邮件?我希望在进行测试时停止接收电子邮件...

【问题讨论】:

  • 您是从脚本中发送电子邮件吗?默认情况下cron不会将邮件输出给crontab的所有者吗?
  • 我正在从内部发送电子邮件,但我需要发送输出。我没有意识到 cron 是这样做的。
  • 嘿,在“邮件”中使用实际的外发电子邮件,而不仅仅是使用 unix 邮件系统。

标签: bash cron


【解决方案1】:

您可以尝试“tty”来查看它是否由终端运行。这不会告诉您它是由 cron 专门运行的,但您可以判断它是否“不是作为提示的用户”。

您也可以获取您的 parent-pid 并按照它在树上查找 cron,尽管这有点笨拙。

【讨论】:

  • 感谢您的回答!我选择的前一个答案实际上不是我想要的(交互式外壳)。由于您的回答,我通过谷歌搜索“bash tty test”找到了我真正需要的东西。基本上,在你的脚本中调用tty -s,然后检查$?:如果为0,你在tty,如果大于0,你不在。
  • tty 是一个外部命令(被授予 coreutils 的一部分,但仍然是一个 fork),有几种方法可以使用内置的东西来做同样的事情 unix.stackexchange.com/a/46801/103306
【解决方案2】:

我遇到了类似的问题。我通过检查 stdout 是否是 TTY 来解决它。这是检查您的脚本是否在交互模式下运行:

if [ -t 1 ] ; then 
    echo "interacive mode";
else
    #send mail
fi

我来自:How to detect if my shell script is running through a pipe?

如果文件描述符打开并且指向终端,-t 测试返回 true。 '1' 是标准输出。

【讨论】:

    【解决方案3】:

    为什么没有命令行参数,即 -t 用于测试或 -c 用于 cron。

    或者更好:

    -e=email@address.com

    如果未指定,请勿发送电子邮件。

    【讨论】:

    • 我将脚本放在 /etc/cron.daily 中,所以我不能轻易添加参数,但我想我可以将它移动到真正的 crontab 中。我只是希望不必在命令行上添加参数或设置环境变量……尽管:在我的 .bashrc 文件中设置一个 TESTING 环境变量就可以了。感谢您的建议。
    • 将myscript.sh arg1 arg2 arg3 放在/etc/cron.daily 的脚本文件中是没有问题的。我通常不会将我的脚本/二进制文件直接放入 /etc/cron.* 目录中。我通常把它留给执行 cron 作业的脚本(可能是另一个脚本)。然后我可以编写更通用的脚本,并在其他环境中使用。
    【解决方案4】:

    这里有两种不同的选择:

    • 从你的脚本/程序中取出电子邮件,让 cron 处理它。如果您在 crontab 中设置 MAILTO 变量,cron 会将打印出来的任何内容发送到该电子邮件地址。例如:

      MAILTO=youremail@example.com
      # run five minutes after midnight, every day
      5 0 * * *       $HOME/bin/daily.job
      
    • 在您的 crontab 中设置一个环境变量,用于确定是否在 cron 下运行。例如:

      THIS_IS_CRON=1
      # run five minutes after midnight, every day
      5 0 * * *       $HOME/bin/daily.job
      

      在你的脚本中类似

      if [ -n "$THIS_IS_CRON" ]; then echo "I'm running in cron"; else echo "I'm not running in cron"; fi
      

    【讨论】:

      【解决方案5】:

      我知道这个问题很老了,但我刚刚遇到了同样的问题。这是我的解决方案:

      CRON=$(pstree -s $$ | grep -q cron && echo true || echo false)
      

      然后测试

      if $CRON
      then
          echo "Being run by cron"
      else
          echo "Not being run by cron"
      fi
      

      与@eruciform 提到的想法相同 - 按照您的 PID 向上检查 cron 的进程树。

      注意:此解决方案仅适用于 cron,与其他一些解决方案不同,它在脚本以非交互方式运行的任何时候都有效。

      【讨论】:

      • 我喜欢将 var 设置为 true 或 false(命令)的简单性,这样如果 $CRON 可以工作(而不是通常的 if [ "$CRON" ]),但请注意,如果 $CRON由于某种原因最终未定义,给定的版本评估为真。不好。
      • 我也喜欢这个想法,但也看到了不确定回报的风险。我最终得到了一个稍微修改过的版本,在我看来它似乎工作得非常顺利: CRON="$( pstree -s $$ | grep -c cron )" 所以你可以检查 $CRON 是 1 还是 0。跨度>
      【解决方案6】:

      对我有用的是检查$TERM。在 cron 下它是“愚蠢的”,但在 shell 下它是另一回事。在终端中使用 set 命令,然后在 cron 脚本中检查它

      if [ "dumb" == "$TERM" ]
      then
          echo "cron"
      else
          echo "term"
      fi
      

      【讨论】:

      • 我还会检查 $TERM 是否不为空。 [ -n "$TERM" -a "$TERM" == "dumb" ] && echo "interactive" || echo "cron or no tty"
      【解决方案7】:

      我想对这个投票率很高的问题提出一个新的答案。这仅适用于具有 loginctl 的 systemd 系统(例如 Ubuntu 14.10+、RHEL/CentOS 7+),但能够提供比以前提出的解决方案更权威的答案。

      service=$(loginctl --property=Service show-session $(</proc/self/sessionid))
      if [[ ${service#*=} == 'crond' ]]; then
         echo "running in cron"
      fi
      

      总结:当与 systemd 一起使用时,crond(如sshd 和其他)在为用户启动作业时会创建一个新的session。此会话具有一个对于机器的整个正常运行时间来说是唯一的 ID。每个会话都有一些属性,其中之一是启动它的服务的名称。 loginctl 可以告诉我们这个属性的值,它将是“crond当且仅当会话实际上是由 crond 启动的。


      使用环境变量的优势:

      • 无需修改 cron 条目即可添加特殊调用或环境变量
      • 中间进程不可能修改环境变量以产生误报或误报

      tty 测试的优势:

      • 管道、启动脚本等中没有误报

      检查流程树的优势:

      • 名称中也包含 crond 的进程没有误报
      • 如果脚本被否认,则不会出现误报

      【讨论】:

      • 遗憾的是,它像其他 systemd 命令一样冗长且不明显。
      【解决方案8】:

      之前文章中使用的许多命令并非在每个系统上都可用(pstree、loginctl、tty)。这是我目前用作黑名单 DNS 服务器的十年前的 BusyBox/OpenWrt 路由器上唯一对我有用的东西。它运行具有自动更新功能的脚本。从 crontab 运行,它会发送一封电子邮件。

      [ -z "$TERM" ] || [ "$TERM" = "dumb" ] && echo 'Crontab' || echo 'Interactive'
      

      在交互式 shell 中,$TERM-变量为我返回值 vt102。因为@edoceo 提到它对他有用,所以我包括了“愚蠢”的检查。我没有使用 '==' 因为它不是完全可移植的。

      【讨论】:

        【解决方案9】:

        我也喜欢 Tal 的想法,但也看到了不确定回报的风险。我最终得到了一个稍微修改过的版本,在我看来它似乎运行得非常顺利:

        CRON="$( pstree -s $$ | grep -c cron )"

        因此您可以随时检查 $CRON 是 1 还是 0。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-10-01
          • 1970-01-01
          • 1970-01-01
          • 2015-02-22
          • 1970-01-01
          • 1970-01-01
          • 2017-06-07
          • 1970-01-01
          相关资源
          最近更新 更多