我已经实现了一种方法来识别脚本是否正在从 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
中设置一个环境变量如何?
如果它在脚本中设置,则意味着它是从 cron 运行的。
如果您的操作系统正在使用 systemd,您实际上有一个非常可靠的方法来确定您是否正在从 cron 运行。
loginctl show-session "$(</proc/self/sessionid)" | sed -n 's/^Service=//p'
当且仅当进程由 cron 启动时,结果才会是
crond
。
其他方法通常存在误报/漏报:
crond
启动一个包装脚本,然后启动你的脚本,则很难保证 crontab 中设置的环境变量将被保留)crond
的进程实际上都是 cron 守护进程,并且可能会出现误报,因为被拒绝的进程将不再是 crond
的子进程。限制速度的因素可能是 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,我们可能会再次产生误报。