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

时间:2008-10-10 10:37:46

标签: php cron

我正在寻找PHP的方法,以检测脚本是否是从shell上的手动调用(我登录并运行它)运行,或者是否从crontab条目运行。

我有各种用PHP编写的维护类型脚本,我已设置在我的crontab中运行。偶尔,我需要提前手动运行它们,或者如果出现故障/损坏,我需要运行它们几次。

这个问题是我也有一些外部通知设置到任务(发布到Twitter,发送电子邮件等),我不想每次手动运行脚本时发生。

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

有什么想法吗?

22 个答案:

答案 0 :(得分:43)

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

从命令行运行脚本时,会设置很多环境变量(在$ _ENV数组中)。这些将根据您的服务器设置和您的登录方式而有所不同。在我的环境中,手动运行脚本时设置以下环境变量,这些变量在从cron运行时不存在:

  • TERM
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

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

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

答案 1 :(得分:31)

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";   
}

答案 2 :(得分:29)

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

CRON=running

然后您可以检查环境变量中的“CRON”。另外,尝试检查$ SHELL变量,我不确定cron是否将其设置为。

答案 3 :(得分:26)

这是我用来发现脚本执行位置的方法。查看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 (可能是 cli cli_server ),那么我们会检查$_SERVER['REMOTE_ADDR']是否为空。从命令行调用时,它应为空。

答案 4 :(得分:15)

我认为最通用的解决方案是在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";

答案 5 :(得分:15)

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

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

答案 6 :(得分:6)

我不是特别了解PHP,但你可以走上进程树,直到找到init或cron。

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

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

例如,这是我的流程树,您可以看到所有权链,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

这个“走上进程树”解决方案意味着您不必担心引入一个人工参数来指示您是否在cron下运行 - 您可能忘记在交互式会话中执行此操作并填充内容起来。

答案 7 :(得分:5)

我不知道 - 可能最简单的解决方案是自己提供一个额外的参数来告诉脚本如何调用它。

答案 8 :(得分:5)

爬行。试试

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

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

答案 9 :(得分:4)

我会调查$_ENV(var_dump()它)并检查你在运行它时是否注意到与cronjob运行它时的区别。除此之外我不认为有一个“官方”开关告诉你发生了什么。

答案 10 :(得分:3)

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

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

答案 11 :(得分:2)

getenv('TERM')

填充SO的30分钟。

答案 12 :(得分:1)

另一种选择是测试通过Web调用php文件时设置的特定环境变量,如果命令行运行则不设置。

在我的网络服务器上,我正在测试APACHE_RUN_DIR环境变量是否设置如下:

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

比较差异并测试适当的变量。

答案 13 :(得分:1)

如果从CLI运行,

$_SERVER['SESSIONNAME']包含Console。也许这有帮助。

答案 14 :(得分:1)

在cron命令中,将?source=cron添加到脚本路径的末尾。然后,在您的脚本中,选中$_GET['source']

编辑:对不起,这是一个shell脚本,所以不能使用qs。我认为,您可以通过php script.php arg1 arg2形式传递参数,然后使用$argv阅读它们。

答案 15 :(得分:0)

在我的Amazon Linux服务器上,这对我有用:

$ isCron =($ _SERVER ['HOME'] =='/');

如果只运行主目录,则将其设置为您的主目录。如果使用sudo来运行它,则主目录设置为/ root。

答案 16 :(得分:0)

(array) $argv 将在 cron 作业运行时设置。 数组的第一个值将是您在创建 cron 作业时使用的 /path/to/file。 $argv 中的以下值将是跟在 /path/to/file 之后的任何参数。

例如,如果您的 cron 作业命令如下所示:

php /path/to/file.php first second third

$argv 的值为:["/path/to/file.php", "first", "second", "third"]

然后你可以:

if (isset($argv) && is_array($argv) && in_array('first', $argv)) { /* do something */ }

答案 17 :(得分:0)

这很容易。 Cron Daemons始终导出MAILTO环境变量。检查它是否存在并且具有非空值 - 然后从cron运行。

答案 18 :(得分:0)

posix_isatty(STDOUT) return FALSE如果cli调用的输出被重定向(管道或文件)......

答案 19 :(得分:0)

我认为最好在命令行运行cron命令,而不是手动运行。

cron会这样做:

command ext_updates=1

手册可以:

command 

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

答案 20 :(得分:0)

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

答案 21 :(得分:-4)

这对我来说很容易......只是count($_SERVER['argc'])如果你的结果高于零,它将会耗尽服务器。您只需添加自$_SERVER['argv']自定义变量,例如"CronJob"=true;