63

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

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

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

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

有任何想法吗?

4

22 回答 22

47

与其检测脚本何时从 crontab 运行,不如检测您何时手动运行它可能更容易。

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

  • 学期
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

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

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

于 2008-10-10T13:31:35.367 回答
45
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";   
}
于 2012-03-19T05:46:58.337 回答
30

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

CRON=running

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

于 2008-10-10T10:47:28.703 回答
30

这是我用来发现脚本执行位置的方法。查看 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']为空。从命令行调用时,这应该是空的。

于 2011-08-26T15:33:07.430 回答
17

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

if (posix_isatty(STDOUT))
    /* do interactive terminal stuff here */
于 2010-03-11T22:15:59.260 回答
17

我认为最通用的解决方案是在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";
于 2011-10-23T19:46:09.277 回答
6

我不具体了解 PHP,但您可以沿着进程树向上走,直到找到 init 或 cron。

假设 PHP 可以获取它自己的进程 ID 并运行外部命令,则应该执行ps -ef | grep pidpid您自己的进程 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 下运行 - 您可能会忘记在交互式会话中执行此操作并进行填充。

于 2008-10-10T11:10:44.553 回答
6

爬行。尝试

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

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

于 2011-04-15T05:03:43.903 回答
5

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

于 2008-10-10T10:39:58.487 回答
5

我会调查$_ENV(var_dump() it) 并检查您在运行它时是否注意到与 cronjob 运行它时的差异。除此之外,我认为没有“官方”开关可以告诉您发生了什么。

于 2008-10-10T10:43:53.953 回答
4

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

if (isset($_SERVER{'TERM'}))
{
    class::doStuffShell();
}
else
{
    class::doStuffWeb();
}
于 2009-05-01T22:59:03.887 回答
3
getenv('TERM')

填充 SO 的 30 字符分钟。

于 2013-08-23T20:27:13.500 回答
2

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

编辑:抱歉,这是一个 shell 脚本,所以不能使用 qs。我认为,您可以在表单中传递参数,php script.php arg1 arg2然后使用$argv.

于 2008-10-10T10:46:30.610 回答
2

另一种选择是测试通过 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

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

于 2014-03-03T15:54:02.840 回答
1

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

于 2009-01-22T23:35:07.297 回答
0
if(!$_SERVER['HTTP_HOST']) {
 blabla();
}
于 2009-01-30T20:03:48.810 回答
0

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

cron 会做:

command ext_updates=1

手册会做:

command 

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

于 2009-04-27T18:13:04.060 回答
0

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

于 2010-11-03T16:18:06.820 回答
0

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

于 2012-06-23T05:49:35.043 回答
0

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

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

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

于 2019-03-06T19:33:50.237 回答
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 */ }

于 2021-07-22T20:42:34.810 回答
-4

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

于 2011-01-02T06:17:30.967 回答