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


23

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

我怎么知道Bash脚本是否由cron启动?


你为什么不只是我们ps
terdon


@terdon:可能是因为ps记录不充分(尤其是Linux的版本,它支持几种不同的语法样式),并且手册页比大多数工具更加密集和神秘。我怀疑大多数人甚至都没有意识到工具的实用性和多功能性ps
cas

Answers:


31

我不知道cron默认情况下可以对它的环境执行任何操作,但是可以做一些事情以获得所需的效果。

1)建立与脚本文件的硬链接或软链接,例如,myscript然后myscript_via_cron指向同一文件。然后,$0当您想要有条件地运行或忽略代码的某些部分时,可以测试脚本内部的值。在您的crontab中输入适当的名称,即可设置好。

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

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


7
RUN_BY_CRON = true的+1
cas

cas给出的答案非常有效,也可以用于其他任何事情
Deian

19

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

方法1:检查是否$-包含该i标志。 i为交互式外壳设置。

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

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

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

参考:http : //techdoc.kvindesland.no/linux/gnubooks/bash/bashref_54.html

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

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

请记住,您可以使用来强制使用交互式外壳-i,但是您可能会知道是否正在执行此操作...


1
请注意,检查脚本是否由systemd启动时,$ PS1命令不起作用。在$ -一个不
mveroone

1
您的温尼伯大学链接已断开。
WinEunuuchs2Unix

1
@TimKennedy不客气....来自埃德蒙顿:)
WinEunuuchs2Unix

'case“ $-” in'在bash脚本中似乎不起作用。
霍巴迪

@Hobadee-每个bash我可以访问的都有$-,dash以及和ksh。甚至Solaris中受限制的Shell都有它。您要在无法使用的平台上使用它?是什么case "$-" in *i*) echo true ;; *) echo false ;; esac给你?
肯尼迪

7

首先,获取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}"

1
在Solaris上,cron启动一个外壳,该外壳运行脚本,脚本本身启动另一个外壳。因此,脚本中的父pid不是cron的pid。
ceving

4

如果脚本文件是由调用的,cron并且它在第一行中包含一个shell,例如#!/bin/bash您需要找到用于目的的父/母名称。

1)cron在您的给定时间调用crontab,执行shell 2)shell执行您的脚本3)您的脚本正在运行

父PID在bash中可用作变量$PPIDps获取父PID的父PID 的命令是:

PPPID=`ps h -o ppid= $PPID`

但是我们需要命令的名称而不是pid,因此我们调用

P_COMMAND=`ps h -o %c $PPPID`

现在我们只需要测试结果“ cron”

if [ "$P_COMMAND" == "cron" ]; then
  RUNNING_FROM_CRON=1
fi

现在您可以在脚本中的任何位置进行测试

if [ "$RUNNING_FROM_CRON" == "1" ]; then
  ## do something when running from cron
else
  ## do something when running from shell
fi

祝好运!


这仅适用于Linux ps。对于MacOS(以及Linux,也许还有* BSD),您可以使用以下P_COMMAND:P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
mdd

1

在FreeBSD或Linux上均可使用:

if [ "Z$(ps o comm="" -p $(ps o ppid="" -p $$))" == "Zcron" -o \
     "Z$(ps o comm="" -p $(ps o ppid="" -p $(ps o ppid="" -p $$)))" == "Zcron" ]
then
    echo "Called from cron"
else
    echo "Not called from cron"
fi

您可以根据需要将进程树向上移动。


1

“我的输出是终端还是我正在从脚本运行”这个问题的通用解决方案是:

( : > /dev/tty) && dev_tty_good=y || dev_tty_good=n

0

echo $TERM | mail me@domain.comcron中的一个简单示例告诉我,在Linux和AIX上,cron似乎都设置$TERM为“哑”。

从理论上讲,现在仍然可能有实际的哑终端,但是我怀疑在大多数情况下,这应该足够了。


0

没有权威性的答案,但在这里,提示($PS1)和终端($TERM)变量相当不错。一些系统已设置,TERM=dumb而大多数系统将其保留为空,因此我们仅检查以下任一情况:

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

当没有的值时,以上代码将替换“哑巴”一词$TERM。因此,条件查询在没有$TERM$TERM设置为“哑”或$PS1变量不为空时触发。

我已经在Debian 9(TERM=),CentOS 6.4和7.4(TERM=dumb)和FreeBSD 7.3(TERM=)上进行了测试。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.