检查脚本是否由 cron 启动,而不是手动调用

检查脚本是否由 cron 启动,而不是手动调用

cron 在运行程序时是否设置任何变量?如果脚本是由 cron 运行的,我想跳过一些部分;否则调用这些部分。

我如何知道 Bash 脚本是否由 cron 启动?

答案1

我不知道cron默认情况下会对其环境执行任何可以在此处使用的操作,但是您可以执行一些操作来获得所需的效果。

1) 创建到脚本文件的硬链接或软链接,以便例如myscriptmyscript_via_cron指向同一个文件。然后$0,当您想要有条件地运行或省略代码的某些部分时,可以测试脚本内部的值。在您的 crontab 中输入适当的名称,然后就完成了。

2) 向脚本添加一个选项,并在 crontab 调用中设置该选项。例如,添加一个选项-c,告诉脚本运行或省略代码的适当部分,并添加-c到 crontab 中的命令名称。

当然,还有 cron设置任意环境变量,因此您只需在 crontab 中添加一行RUN_BY_CRON="TRUE",然后在脚本中检查其值。

答案2

从 cron 运行的脚本不在交互式 shell 中运行。启动脚本也不是。区别在于交互式 shell 将 STDIN 和 STDOUT 附加到 tty。

方法一:检查是否$-包含iflag。 i是为交互式 shell 设置的。

case "$-" in
    *i*)
        interactive=1
        ;;
    *)
        not_interactive=1
        ;;
esac

方法二:检查是否$PS1为空。

if [ -z "$PS1" ]; then
    not_interactive=1 
else
    interactive=1
fi

参考:

方法3:测试你的tty。它不是作为可靠,但对于简单的 cron 作业,你应该没问题,因为 cron 默认情况下不会为脚本分配 tty。

if [ -t 0 ]; then
    interactive=1
else
    non_interactive=1
fi

请记住,您可以使用强制交互式 shell -i,但如果您这样做,您可能会意识到...

答案3

首先获取cron的PID,然后获取当前进程的父进程PID(PPID),并比较它们:

CRONPID=$(ps ho %p -C cron)
PPID=$(ps ho %P -p $$)
if [ $CRONPID -eq $PPID ] ; then echo Cron is our parent. ; fi

如果您的脚本是由另一个可能由 cron 启动的进程启动的,那么您可以逐步返回父 PID,直到到达 $CRONPID 或 1(init 的 PID)。

也许是这样的(未经测试但可能有效<TM>):

PPID=$$   # start from current PID
CRON_IS_PARENT=0
CRONPID=$(ps ho %p -C cron)
while [ $CRON_IS_PARENT -ne 1 ] && [ $PPID -ne 1 ] ; do
  PPID=$(ps ho %P -p $PPID)
  [ $CRONPID -eq $PPID ] && CRON_IS_PARENT=1
done

来自 Deian:这是在 RedHat Linux 上测试的版本

# start from current PID
MYPID=$$
CRON_IS_PARENT=0
# this might return a list of multiple PIDs
CRONPIDS=$(ps ho %p -C crond)

CPID=$MYPID
while [ $CRON_IS_PARENT -ne 1 ] && [ $CPID -ne 1 ] ; do
        CPID_STR=$(ps ho %P -p $CPID)
        # the ParentPID came up as a string with leading spaces
        # this will convert it to int
        CPID=$(($CPID_STR))
        # now loop the CRON PIDs and compare them with the CPID
        for CRONPID in $CRONPIDS ; do
                [ $CRONPID -eq $CPID ] && CRON_IS_PARENT=1
                # we could leave earlier but it's okay like that too
        done
done

# now do whatever you want with the information
if [ "$CRON_IS_PARENT" == "1" ]; then
        CRON_CALL="Y"
else
        CRON_CALL="N"
fi

echo "CRON Call: ${CRON_CALL}"

答案4

没有权威的答案。这里的其他一些答案实际上尝试匹配 cron 环境,这是一项艰巨的任务。

就我个人而言,我只是在我的 crontab 中设置一个变量,例如CRON='in_cron'在顶部。然后你可以像这样测试它:

if [ "$CRON" != "in_cron" ]; then
  echo "This is not a cron job"
fi

这将权威地区分 Interactive/nohup/ssh 和 cron,但它确实需要在相关用户的 crontab 中声明该变量。对于 /etc/cron.d 脚本,您应该能够将其放入 /etc/crontab (至少在我的 Debian 12 系统上有效)。


终端 ( $TERM) 变量在这里相当不错,shell 的“交互式”选项标志也是如此(特殊参数 $-包含i)。有些系统设置了TERM=dumb,而大多数系统将其留空,因此我们只检查其中一个,并检查交互式选项标志:

if [ "${TERM:-dumb}$-" != "dumb${-#*i}" ]; then
  echo "This is not a cron job"
fi

当 没有值时,上面的代码会替换“dumb”一词$TERM。因此,当没有$TERMor$TERM设置为“哑”或者$PS1变量不为空或者 if$-在删除第一个字符之前的所有字符时与自身不匹配时,条件将触发i(当没有 no 时,这不会删除任何内容i)。

我已经在 Debian 9、11 和 12 ( TERM=)、CentOS 6.4 和 7.4 ( TERM=dumb) 以及 FreeBSD 7.3 && 11.2 ( TERM=) 上对此进行了测试。这曾经额外包含$PS1,但我的 Debian 12 系统的 cron 现在以某种方式设置该变量。

至少在 Debian 12 上,这将区分 Interactive/nohup 和 cron/ssh。


您也可以检查终端名称。 Cron 通常(验证这一点!)不会分配终端,因此您可以运行ttyPOSIX 标准要求(如果它确实没有终端)它必须not a tty在输出中说:

if [ "$(tty)" != "not a tty" ]; then
  echo "This is not a cron job"
fi

至少在 Debian 12 上,这将区分交互式和 cron/ssh/nohup。


另一个答案提到( : > /dev/tty) 2>/dev/null匹配交互性。

至少在 Debian 12 上,这将区分 Interactive/nohup 与 cron/ssh。


利用 cron 不分配终端的第四种方法就是测试标准输入是否打开。您可以使用 来执行此操作! [ -t 0 ],但如果您将数据通过管道传输到脚本中,这会给您一个不正确的答案。

相关内容