PHP可以检测它是否从cron作业或从命令行运行?

我正在寻找方式来检测是否一个脚本是从一个shell(我login和运行它)手动调用运行,或者如果它从crontab条目运行。

我有用PHP编写的各种维护types的脚本,我已经设置在我的crontab中运行。 偶尔,我需要提前手动运行它们,或者如果某些事件发生故障/中断,我需要运行它们几次。

与此问题是,我也有一些外部通知设置到任务(张贴到Twitter,发送电子邮件等),我不希望每当我手动运行脚本发生。

我使用php5(如果它很重要),它是一个相当标准的Linux服务器环境。

有任何想法吗?

而不是检测脚本从crontab运行的时间,它可能更容易检测到,当你手动运行它。

从命令行运行脚本时,会设置许多环境variables(在$ _ENV数组中)。 这些将取决于您的服务器设置以及您的login方式。根据您的环境,在从cron运行时手动运行脚本时不会出现以下环境variables:

  • 术语
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

还有其他人。 因此,例如,如果您始终使用SSH访问该框,则以下行将检测脚本是否从cron运行:

$cron = !isset($_ENV['SSH_CLIENT']);

你可以设置一个额外的参数,或者在你的crontab中添加一行,也许:

 CRON=running 

然后你可以检查你的环境variables“CRON”。 此外,请尝试检查$ SHELLvariables,我不确定是否/什么cron设置它。

这是我用来发现脚本从哪里执行的。 查看php_sapi_name函数获取更多信息: http ://www.php.net/manual/en/function.php-sapi-name.php

 $sapi_type = php_sapi_name(); if(substr($sapi_type, 0, 3) == 'cli' || empty($_SERVER['REMOTE_ADDR'])) { echo "shell"; } else { echo "webserver"; } 

编辑:如果php_sapi_name()不包括cli (可能是clicli_server ),那么我们检查$_SERVER['REMOTE_ADDR']是否为空。 从命令行调用时,应该是空的。

 if (php_sapi_name() == 'cli') { if (isset($_SERVER['TERM'])) { echo "The script was run from a manual invocation on a shell"; } else { echo "The script was run from the crontab entry"; } } else { echo "The script was run from a webserver, or something else"; } 

正确的做法是在例如stdout文件描述符上使用posix_isatty()函数,如下所示:

 if (posix_isatty(STDOUT)) /* do interactive terminal stuff here */ 

我认为最通用的解决scheme是将一个环境variables添加到cron命令,并在代码中查找它。 它将在每个系统上工作。

如果cron执行的命令是,例如:

 "/usr/bin/php -q /var/www/vhosts/myuser/index.php" 

将其更改为

 "CRON_MODE=1 /usr/bin/php -q /var/www/vhosts/myuser/index.php" 

那么你可以检查它的代码:

 if (!getenv('CRON_MODE')) print "Sorry, only CRON can access this script"; 

我不明白PHP的具体情况,但是你可以走上进程树,直到findinit或者cron。

假设PHP可以得到它自己的进程ID并运行外部命令,它应该是执行ps -ef | grep pid ps -ef | grep pid其中, pid是您自己的进程ID,并从中提取父进程ID(PPID)。

然后对该PPID执行相同的操作,直到您作为父代或cron作为父代。

例如,这是我的stream程树,您可以看到所有权链,1 – > 6386 – > 6390 – > 6408。

 UID PID PPID C STIME TTY TIME CMD root 1 0 0 16:21 ? 00:00:00 /sbin/init allan 6386 1 0 19:04 ? 00:00:00 gnome-terminal --geom... allan 6390 6386 0 19:04 pts/0 00:00:00 bash allan 6408 6390 0 19:04 pts/0 00:00:00 ps -ef 

在cron下运行的过程如下所示:

 UID PID PPID C STIME TTY TIME CMD root 1 0 0 16:21 ? 00:00:00 /sbin/init root 5704 1 0 16:22 ? 00:00:00 /usr/sbin/cron allan 6390 5704 0 19:04 pts/0 00:00:00 bash allan 6408 6390 0 19:04 pts/0 00:00:00 ps -ef 

这个“走进过程树”的解决scheme意味着你不必担心引入一个假的参数来表明你是否在cron下运行 – 你可能会忘记在你的交互式会话中做这件事,

不是我所知 – 可能最简单的解决scheme是提供一个额外的参数来告诉脚本如何被调用。

我会研究$_ENV (var_dump()它),并检查当你运行它时,与cronjob运行时是否有差异。 除此之外,我不认为有一个“官方”的开关告诉你发生了什么事情。

在我的环境中,我发现如果从命令行运行, TERM被设置在$_SERVER ,但是如果通过Apache作为Web请求运行,则不会设置。 我把它放在我可能从命令行运行的脚本的顶部,或者可以通过Web浏览器访问:

 if (isset($_SERVER{'TERM'})) { class::doStuffShell(); } else { class::doStuffWeb(); } 

爬行。 尝试

 if (!isset($_SERVER['HTTP_USER_AGENT'])) { 

代替。 PHP的客户端二进制文件不发送它。 Termtypes只在PHP用作模块(即apache)时有效,但通过CGI接口运行php时,请使用上面的示例!

在cron命令中,将?source=cron添加到脚本path的末尾。 然后,在你的脚本中,检查$_GET['source']

编辑:对不起,这是一个shell脚本,所以不能使用qs。 你可以,我想,以php script.php arg1 arg2的forms传递参数,然后用$argv读取它们。

 getenv('TERM') 

填补SO的30个字符分钟。

$_SERVER['SESSIONNAME']包含从CLI运行的控制台。 也许这有帮助。

另一个select是testing一个特定的环境variables,这个variables是通过web调用php文件时设置的,而不是由命令行运行时设置的。

在我的Web服务器上,我正在testingAPACHE_RUN_DIR环境variables是否如下设置:

 if (isset($_ENV["APACHE_RUN_DIR"])) { // I'm called by a web user } else { // I'm called by crontab } 

为了确保它能在你的Web服务器上工作,你可以用下面的语句在你的Web服务器上放一个虚拟的php文件:

 <?php var_dump($_ENV); ?> 

然后1)用你的网页浏览器加载它,2)像这样从命令行加载它

 /usr/bin/php /var/www/yourpath/dummy.php 

比较差异并testing适当的variables。

如果cli调用的输出被redirect(pipe道或文件), posix_isatty(STDOUT) return FALSE

 if(!$_SERVER['HTTP_HOST']) { blabla(); } 

我认为最好是在命令行中使用额外的选项来运行cron命令,而不是手动运行。

克朗会做:

 command ext_updates=1 

手册会做:

 command 

只需在脚本中添加一个选项,让ext_updates参数的默认值为false。

这很容易。 Cron守护进程总是导出MAILTO环境variables。 检查它是否存在并具有非空值 – 然后从cron运行。

对我来说很简单…只需要count($_SERVER['argc']) ,如果结果高于零,它将耗尽服务器。 你只需要添加到你的$_SERVER['argv']你的自定义variables,如"CronJob"=true;