确定 Bash 脚本是否从 cron 运行的快速方法

问题描述 投票:0回答:3

我已经实现了一种方法来识别脚本是否正在从 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
}
bash shell cron
3个回答
2
投票

在运行脚本之前在

crontab
中设置一个环境变量如何?

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


1
投票

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

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

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

crond


其他方法通常存在误报/漏报:

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

0
投票

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

不安全版本

如果有其他进程包含字符串

cron
,此版本可能会导致误报。我认为你的脚本也有同样的问题。

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

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

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

更安全的版本

我们不使用

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
    。如果这样的进程是当前进程的祖先,并且
    123
    cron
    的 PID,我们可能会再次产生误报。

© www.soinside.com 2019 - 2024. All rights reserved.