【问题标题】:Fast way to determine if Bash script is run from cron确定 Bash 脚本是否从 cron 运行的快速方法
【发布时间】:2017-06-07 11:46:28
【问题描述】:

我已经实现了一种方法来识别脚本是否从 cron 作业运行。它使用ps 爬升进程树并识别是否有任何(递归)父命令包含“cron”或“CRON”。

这个解决方案在工作时速度很慢(比如大约一秒钟),并且每次我调用它们时都会影响我拥有的所有脚本。我正在寻找更快的解决方案。

我不想在 crontab 内的脚本中添加任何选项,因为我的目标是在命令行上没有提供通知选项时精确定义默认通知行为,并使该默认行为与 cron 作业不同。

是否有一种相当可靠、快速的方法来确定脚本(或其递归父级之一)是否是从 cron 作业启动的?

在我最初的帖子之后,我重新编写了我的代码并能够对其进行改进,尽管ps 仍在使用中。代码如下,欢迎提出建议。

is_cron_job()
{
  local PS
  local CMD
  local PID=$$
  while :
  do
    PS="$(ps -h -o ppid,comm -p $PID)"
    [[ "$PS" =~ ^[[:space:]]*([0-9]+)[[:space:]]+(.*)$ ]] || return 1
    PID="${BASH_REMATCH[1]}"
    [[ "$PID" -ge 1 ]] || return 1
    CMD="${BASH_REMATCH[2]}"
    ! [[ "$CMD" =~ crond|CROND ]] || return 0
  done
  return 1
}

【问题讨论】:

  • 您可以在crontab 文件中设置一个环境变量,并在您的脚本中检查它。
  • This question 询问 PHP,但大多数答案也适用于 bash 脚本。简而言之,不,默认情况下,cron 本身不会给您任何标识自己的信息,但是很容易检测到交互式使用;例如如果 stdout 和 stdin 都连接到终端,[[ -t 0 && -t 1 ]] 将评估为 true。
  • 也许这是一个 XY 问题?从 cron 运行有什么特别之处?也许您应该使用更容易测试的不同标准?
  • “以防我的脚本安装在我无法控制的服务器上”。安装说明应该说设置变量。或者你应该提供一个安装脚本。
  • 那么我认为你被 ps 解决方案所困扰,或者可能是使用 /proc 来做类似的事情。但这没有简单的方法。

标签: bash shell cron


【解决方案1】:

在运行脚本之前在crontab中设置一个环境变量怎么样?

如果它是在脚本中设置的,这意味着它是从 cron 运行的。

【讨论】:

    【解决方案2】:

    如果您的操作系统使用的是 systemd,那么您实际上有一种非常可靠的方法可以确定您是否从 cron 运行。

    loginctl show-session $(</proc/self/sessionid) | sed -n '/^Service=/s/.*=//p'
    

    当且仅当进程由 cron 启动时,结果才会为 crond


    其他方法通常有误报/误报:

    • 环境变量可以由中间进程操作(例如,如果crond 启动一个包装脚本,然后启动您的脚本,则很难保证在 crontab 中设置的环境变量会被保留)
    • 涉及测试交互式 shell 或 tty 存在的方法在管道、启动脚本等条件下会出现误报。
    • 依赖于解析进程树的方法可能会出现误报,因为并非所有名为 crond 的进程实际上 都是 cron 守护进程,并且可能会出现误报,因为被取消的进程将不再是crond

    【讨论】:

      【解决方案3】:

      限制速度因素可能是 bash 循环,因为 bash(与其他 shell 一样)被解释且缓慢。以下命令避免了循环,可能会更快。

      不安全的版本

      如果有其他进程包含字符串cron,此版本可能会导致误报。我认为你的脚本有同样的问题。

      function is_cron_job {
          pstree -s $$ | grep -Fip 'cron'
      }
      

      pstree -s $$ 仅打印当前进程及其祖先(即它们的名称)。

      grep -Fip 'cron' 搜索固定 (-F)、不区分大小写 (-i) 的字符串 "cron",如果匹配则返回 0,如果不匹配则返回 1 (@987654330 @)。

      更安全的版本

      我们不使用cron 的进程名称,而是搜索它的PID。

      function is_cron_job {
          local cronpid="$(pgrep cron)"
          if [ $(wc -w <<< "$cronpid") -ne 1 ]; then
              echo "No or multiple PIDs for cron!"
              exit 1
          fi
          pstree -sp $$ | grep -Po '\(\d+\)' | grep -Fq "($cronpid)"
      }
      

      待解决的问题:

      • 以可靠的方式确定cron的PID。

      • 以安全的方式从 pstree 中提取 PID。
        这将是非常不常见的,但可以将进程命名为something(123)funny。如果这样的进程是当前进程的祖先并且123crons PID,我们可能会再次产生误报。

      【讨论】:

      • 您的两个解决方案都使用pstree,这在我工作的操作系统上不可用。您更安全的解决方案使用外部程序和命令替换(以及子shell)的多次调用,所以它不是我希望的那种“快速”。
      • 话虽如此,我的代码确实可能会导致误报,但我认为这种风险是合理的,因为不仅系统上必须有许多匹配进程,而且我的脚本也会有成为其中一个的孩子(除了真正的cron),这对我来说似乎不太可能。如果发生这种情况,我的行为可能会有所不同,但不会发生崩溃或数据丢失,所以没什么大不了的。
      猜你喜欢
      • 2013-11-26
      • 2011-08-10
      • 1970-01-01
      • 1970-01-01
      • 2010-09-14
      • 2011-03-13
      • 2012-10-22
      • 2010-10-23
      • 2017-05-28
      相关资源
      最近更新 更多