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

问题描述:

我正在寻找方式来检测PHP脚本是否从shell上的手动调用(我登录并运行它)运行,或者它是否从crontab条目运行。PHP可以检测它是否从cron作业或命令行运行?

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

问题在于,我还有一些外部通知设置到任务中(张贴到Twitter,发送电子邮件等),我不希望每次手动运行脚本时都会发生这种情况。

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

任何想法?

+2

作为链接问题添加:[检测PHP脚本是否正在交互式运行](http://stackoverflow.com/questions/11327367/detect-if-a-php-script-is-being-run -interactively有或没有/ 11327451) – Leigh 2012-07-04 11:30:20

+0

@Leigh:感谢您的参考! – hakre 2015-11-06 11:04:32

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

当您从命令行运行脚本时,会设置很多环境变量(在$ _ENV数组中)。这些是什么,将取决于您的服务器设置和手动运行脚本时从cron运行时,不存在你的登录方式在我的环境,下面的环境变量设置:

  • TERM
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

有别人了。因此,举例来说,如果你总是使用SSH访问的方块,然后如果脚本从cron运行下面一行将检测:

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

+1

查看后,这似乎是最好的方法。在_ENV + _SERVER之间,我很确定我可以自信地检测到谁/在哪里/谁在运行它,并据此采取行动。 CRON = crontab中的运行线也会有帮助。 – Uberfuzzy 2008-10-10 14:15:32

+1

似乎默认的php.ini已经随着时间的推移而改变,不会默认填充$ _ENV全局。一个安全的方法是使用getenv('SSH_CLIENT') - 在这里看到更多的信息http://stackoverflow.com/questions/3780866/why-is-my-env-empty – GuruBob 2015-09-17 20:53:46

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

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

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

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

+2

这是一个shell脚本,而不是一个网页。 – 2008-10-10 10:50:20

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

CRON=running 

然后你就可以检查你的环境变量“CRON”。另外,尝试检查$ SHELL变量,我不确定/ cron设置它。

+0

我从来不知道你可以把其他命令/变量行放在crontab中。我只学过定时命令的语法。这加上PHP的$ _ENV和$ _SERVER,将有助于我想要实现的。谢谢 – Uberfuzzy 2008-10-10 15:44:21

+0

“男人5 crontab”为所有坚韧的细节。但是,您可以设置环境变量。通常这是用来设置PATH或SHELL,但你可以设置你喜欢的任何东西。 – 2008-10-10 21:48:28

+1

只是想指出,`variables_order`必须包括“E”在PHP。ini以便为$ _ENV数组填充 – andrewtweber 2012-04-12 21:19:23

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

假设PHP可以获得它自己的进程ID和运行外部命令,就应该执行ps -ef | grep pid的无论身在何处PID是你自己的进程ID,并从中提取父进程ID(PPID)。

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

例如,这是我的进程树,你可以看到所有权链,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下运行 - 您可能会忘记在交互式会话中执行此操作,填满东西。

+0

大概你可以用'无参数'这种方式来设置它,这意味着你正在交互式运行,这样你就不会忘记它。虽然有趣的解决方案。 – 2008-10-10 11:24:55

+0

是的,非常有趣的做法。也可以使用其他非PHP的东西。 – Uberfuzzy 2008-10-10 13:48:43

$_SERVER['SESSIONNAME']包含Console如果从CLI运行。也许这有帮助。

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

我认为这将是更好地与你不会手动运行命令行的附加选项运行的cron条命令。

的cron会做:

command ext_updates=1 

手册会做:

command 

只需添加脚本本身的选项有ext_updates参数去有假的默认值。

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

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

正确的方法是使用上的例如posix_isatty()函数标准输出文件描述符,像这样:

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

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

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

令人毛骨悚然。尝试

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

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

这是我用来发现其中脚本是从执行。看看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']是空的。从命令行调用时,它应该是空的。

我认为最普遍的解决方案是一个环境变量添加到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"; 

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

这很简单。 Cron守护程序始终导出MAILTO环境变量。检查它是否存在并具有非空值 - 然后从cron运行。

getenv('TERM') 

填充SO的30个字符分钟。

另一种选择是测试当PHP文件是通过Web调用,如果通过命令行拼命地跑不设置设置一个特定的环境变量。

。如果APACHE_RUN_DIR环境变量设置这样我测试我的Web服务器:

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

,以确保它会在您的Web服务器上运行,你可以把一个虚拟的PHP文件的网站服务器与此单个语句:

<?php var_dump($_ENV); ?> 

然后1)使用Web浏览器加载以及2)命令行加载像这样

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

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