Can PHP detect if its run from a cron job or from the command line?

PhpCron

Php Problem Overview


I'm looking for way to PHP to detect if a script was run from a manual invocation on a shell (me logging in and running it), or if it was run from the crontab entry.

I have various maintenance type scripts written in php that i have set to run in my crontab. Occasionally, and I need to run them manually ahead of schedule or if something failed/broken, i need to run them a couple times.

The problem with this is that I also have some external notifications set into the tasks (posting to twitter, sending an email, etc) that I DONT want to happen everytime I run the script manually.

I'm using php5 (if it matters), its a fairly standard linux server environment.

Any ideas?

Php Solutions


Solution 1 - Php

Instead of detecting when the script is run from the crontab, it's probably easier to detect when you're running it manually.

There are a lot of environment variables (in the $_ENV array) that are set when you run a script from the command line. What these are will vary depending on your sever setup and how you log in. In my environment, the following environment variables are set when running a script manually that aren't present when running from cron:

  • TERM
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

There are others too. So for example if you always use SSH to access the box, then the following line would detect if the script is running from cron:

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

Solution 2 - Php

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

Solution 3 - Php

You can setup an extra parameter, or add a line in your crontab, perhaps:

CRON=running

And then you can check your environment variables for "CRON". Also, try checking the $SHELL variable, I'm not sure if/what cron sets it to.

Solution 4 - Php

Here's what I use to discover where the script is executed from. Look at the php_sapi_name function for more information: 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";
}

EDIT: If php_sapi_name() does not include cli (could be cli or cli_server) then we check if $_SERVER['REMOTE_ADDR'] is empty. When called from the command line, this should be empty.

Solution 5 - Php

The right approach is to use the posix_isatty() function on e.g. the stdout file descriptor, like so:

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

Solution 6 - Php

I think that the most universal solution is to add an environment variable to the cron command, and look for it on the code. It will work on every system.

If the command executed by the cron is, for example:

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

Change it to

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

Then you can check it on the code:

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

Solution 7 - Php

I don't know about PHP specifically but you could walk up the process tree until you found either init or cron.

Assuming PHP can get it's own process ID and run external commands, it should be a matter of executing ps -ef | grep pid where pid is your own process ID and extract the parent process ID (PPID) from it.

Then do the same to that PPID until you either reach cron as a parent or init as a parent.

For example, this is my process tree and you can see the ownership chain, 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

The same processes run under cron would look like:

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

This "walking up the process tree" solution means you don't have to worry about introducing an artificial parameter to indicate whether you're running under cron or not - you may forget to do it in your interactive session and stuff things up.

Solution 8 - Php

Creepy. Try

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

instead. PHP Client Binary dont send it. Term Type just works when PHP is used as module (ie apache) but when running php through CGI interface, use the example above!

Solution 9 - Php

Not that I know of - probably the simplest solution is providing an extra parameter yourself to tell the script how it was invoked.

Solution 10 - Php

I would look into $_ENV (var_dump() it) and check if you notice a difference when you run it vs. when the cronjob runs it. Aside from that I don't think there is an "official" switch that tells you what happened.

Solution 11 - Php

In my environment, I found that TERM was set in $_SERVER if run from the command line, but not set if run via Apache as a web request. I put this at the top of my script that I might run from the command line, or might access via a web browser:

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

Solution 12 - Php

getenv('TERM')

Padding for SO's 30 char min.

Solution 13 - Php

In the cron command, add ?source=cron to the end of the script path. Then, in your script, check $_GET['source'].

EDIT: sorry, it's a shell script so can't use qs. You can, I think, pass arguments in the form php script.php arg1 arg2 and then read them with $argv.

Solution 14 - Php

Another option would be to test a specific environment variable that is set when the php file is invoked through the web and not set if runned by the commandline.

On my web server I'm testing if the APACHE_RUN_DIR environment variable is set like this :

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

To make sure it will work on your web server, you can put a dummy php file on your web server with this single statement :

<?php var_dump($_ENV);  ?>

Then 1) load it with your web browser and 2) load it from the commandline like this

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

Compare the differences and test the appropriate variable.

Solution 15 - Php

$_SERVER['SESSIONNAME'] contains Console if run from the CLI. Maybe that helps.

Solution 16 - Php

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

Solution 17 - Php

I think it would be better to run the cron commmand with an additional option at the command line that you wouldn't run manually.

cron would do:

command ext_updates=1

manual would do:

command 

Just add an option in the script itself to have the ext_updates param to have a default value of false.

Solution 18 - Php

posix_isatty(STDOUT) return FALSE if the output of the cli call is redirected (pipe or file)...

Solution 19 - Php

This is very easy. Cron Daemons always export MAILTO environment variable. Check if it exists and has a non-empty value - then you running from cron.

Solution 20 - Php

On my Amazon Linux server this is what worked for me:

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

The home directory is set to yours if you just run it. If you use sudo to run it, the home directory is set to /root.

Solution 21 - Php

(array) $argv will be set when the cron job runs. The first value of the array will be the /path/to/file you used when creating the cron job. The following values in $argv will be any parameters that followed the /path/to/file.

For example, if your cron job command looks like this:

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

The value of $argv will be: ["/path/to/file.php", "first", "second", "third"]

You can then:

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

Solution 22 - Php

It's easy for me... Just count($_SERVER['argc']) and if you have a result higher than zero it will be running out of a server. You just need to add to your $_SERVER['argv'] your custom variable, like "CronJob"=true;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUberfuzzyView Question on Stackoverflow
Solution 1 - PhpPaul StoneView Answer on Stackoverflow
Solution 2 - PhpMingalevMEView Answer on Stackoverflow
Solution 3 - PhpMatthew ScharleyView Answer on Stackoverflow
Solution 4 - PhpdavethebraveView Answer on Stackoverflow
Solution 5 - PhpWouter BolsterleeView Answer on Stackoverflow
Solution 6 - PhpagiView Answer on Stackoverflow
Solution 7 - PhppaxdiabloView Answer on Stackoverflow
Solution 8 - PhpPeterDerMeterView Answer on Stackoverflow
Solution 9 - PhpDominic RodgerView Answer on Stackoverflow
Solution 10 - PhpTillView Answer on Stackoverflow
Solution 11 - PhpDrew StephensView Answer on Stackoverflow
Solution 12 - Phpuser213154View Answer on Stackoverflow
Solution 13 - PhpAdam HopkinsonView Answer on Stackoverflow
Solution 14 - PhpeosphereView Answer on Stackoverflow
Solution 15 - PhpBoukeView Answer on Stackoverflow
Solution 16 - PhpIvorView Answer on Stackoverflow
Solution 17 - PhpChristopher RiveraView Answer on Stackoverflow
Solution 18 - PhpDanielView Answer on Stackoverflow
Solution 19 - PhpGabor GaramiView Answer on Stackoverflow
Solution 20 - PhpPhil MarshallView Answer on Stackoverflow
Solution 21 - PhpiapsView Answer on Stackoverflow
Solution 22 - PhpKurausuView Answer on Stackoverflow