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 (可能是cli或cli_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;