cron 在运行程序时是否设置任何变量?如果脚本是由 cron 运行的,我想跳过一些部分;否则调用这些部分。
我如何知道 Bash 脚本是否由 cron 启动?
答案1
我不知道cron
默认情况下会对其环境执行任何可以在此处使用的操作,但是您可以执行一些操作来获得所需的效果。
1) 创建到脚本文件的硬链接或软链接,以便例如myscript
和myscript_via_cron
指向同一个文件。然后$0
,当您想要有条件地运行或省略代码的某些部分时,可以测试脚本内部的值。在您的 crontab 中输入适当的名称,然后就完成了。
2) 向脚本添加一个选项,并在 crontab 调用中设置该选项。例如,添加一个选项-c
,告诉脚本运行或省略代码的适当部分,并添加-c
到 crontab 中的命令名称。
当然,还有 cron能设置任意环境变量,因此您只需在 crontab 中添加一行RUN_BY_CRON="TRUE"
,然后在脚本中检查其值。
答案2
从 cron 运行的脚本不在交互式 shell 中运行。启动脚本也不是。区别在于交互式 shell 将 STDIN 和 STDOUT 附加到 tty。
方法一:检查是否$-
包含i
flag。 i
是为交互式 shell 设置的。
case "$-" in
*i*)
interactive=1
;;
*)
not_interactive=1
;;
esac
方法二:检查是否$PS1
为空。
if [ -z "$PS1" ]; then
not_interactive=1
else
interactive=1
fi
参考:
- https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html
- https://www.gnu.org/software/bash/manual/html_node/Is-this-Shell-Interactive_003f.html
方法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
。因此,当没有$TERM
or$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 通常(验证这一点!)不会分配终端,因此您可以运行tty
和POSIX 标准要求(如果它确实没有终端)它必须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 ]
,但如果您将数据通过管道传输到脚本中,这会给您一个不正确的答案。