web-dev-qa-db-fra.com

Peut PHP détecter si son exécution à partir d'un travail cron ou de la ligne de commande?

Je cherche un moyen de PHP pour détecter si un script a été exécuté à partir d'une invocation manuelle sur un shell (je me connecte et l'exécute), ou s'il a été exécuté à partir de l'entrée crontab.

J'ai divers scripts de type de maintenance écrits en php que j'ai définis pour fonctionner dans mon crontab. Parfois, et je dois les exécuter manuellement avant la date prévue ou si quelque chose a échoué/cassé, j'ai besoin de les exécuter plusieurs fois.

Le problème avec cela est que j'ai également des notifications externes définies dans les tâches (publication sur Twitter, envoi d'un e-mail, etc.) que je ne veux PAS avoir à chaque fois que j'exécute le script manuellement.

J'utilise php5 (si c'est important), c'est un environnement de serveur linux assez standard.

Des idées?

49
Uberfuzzy

Au lieu de détecter quand le script est exécuté à partir de la crontab, il est probablement plus facile de détecter lorsque vous l'exécutez manuellement.

De nombreuses variables d'environnement (dans le tableau $ _ENV) sont définies lorsque vous exécutez un script à partir de la ligne de commande. Leur nature varie en fonction de la configuration de votre serveur et de la façon dont vous vous connectez. Dans mon environnement, les variables d'environnement suivantes sont définies lors de l'exécution manuelle d'un script qui ne sont pas présentes lors de l'exécution depuis cron:

  • TERM
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

Il y en a aussi d'autres. Ainsi, par exemple, si vous utilisez toujours SSH pour accéder à la boîte, la ligne suivante détectera si le script s'exécute à partir de cron:

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

43
Paul Stone
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";   
}
31
MingalevME

Vous pouvez configurer un paramètre supplémentaire ou ajouter une ligne dans votre crontab, peut-être:

CRON=running

Et puis vous pouvez vérifier vos variables d'environnement pour "CRON". Essayez également de vérifier la variable $ Shell, je ne sais pas si/à quoi cron la définit.

29
Matthew Scharley

Voici ce que j'utilise pour découvrir d'où le script est exécuté. Regardez la fonction php_sapi_name pour plus d'informations: 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: Si php_sapi_name() n'inclut pas cli (pourrait être cli ou cli_server) alors nous vérifions si $_SERVER['REMOTE_ADDR'] est vide. Lorsqu'il est appelé depuis la ligne de commande, il doit être vide.

26
davethebrave

Je pense que la solution la plus universelle consiste à ajouter une variable d'environnement à la commande cron et à la rechercher dans le code. Cela fonctionnera sur tous les systèmes.

Si la commande exécutée par le cron est par exemple:

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

Changez-le en

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

Ensuite, vous pouvez le vérifier sur le code:

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

La bonne approche consiste à utiliser la fonction posix_isatty () sur par exemple le descripteur de fichier stdout, comme ceci:

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

Je ne connais pas PHP spécifiquement mais vous pouvez remonter l'arborescence des processus jusqu'à ce que vous trouviez init ou cron.

En supposant que PHP peut obtenir son propre ID de processus et exécuter des commandes externes, il devrait s'agir d'exécuter ps -ef | grep pidpid est votre propre ID de processus et extrayez l'ID de processus parent (PPID) de celui-ci.

Faites de même pour ce PPID jusqu'à ce que vous atteigniez cron en tant que parent ou init en tant que parent.

Par exemple, ceci est mon arbre de processus et vous pouvez voir la chaîne de propriété, 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

Les mêmes processus exécutés sous cron ressembleraient à:

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

Cette solution "remonter dans l'arborescence des processus" signifie que vous n'avez pas à vous soucier d'introduire un paramètre artificiel pour indiquer si vous exécutez sous cron ou non - vous pouvez oublier de le faire dans votre session interactive et bourrer les choses.

6
paxdiablo

Pas que je sache - probablement la solution la plus simple consiste à fournir vous-même un paramètre supplémentaire pour indiquer au script comment il a été invoqué.

5
Dominic Rodger

Terrifiant. Essayer

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

au lieu. PHP Client binaire ne l'envoie pas. Le type de terme ne fonctionne que lorsque PHP est utilisé comme module (c'est-à-dire Apache) mais lors de l'exécution de php via l'interface CGI, utilisez l'exemple) au dessus!

5
PeterDerMeter

J'examinerais $_ENV (var_dump () it) et vérifiez si vous remarquez une différence lorsque vous l'exécutez par rapport à quand le cronjob l'exécute. En dehors de cela, je ne pense pas qu'il y ait un interrupteur "officiel" qui vous indique ce qui s'est passé.

4
Till

Dans mon environnement, j'ai constaté que TERM était défini dans $_SERVER si exécuté à partir de la ligne de commande, mais non défini s'il est exécuté via Apache en tant que demande Web. Je mets ceci en haut de mon script que je pourrais exécuter à partir de la ligne de commande, ou accéder via un navigateur Web:

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

Rembourrage pour les SO 30 min.

2
user213154

Une autre option serait de tester une variable d'environnement spécifique qui est définie lorsque le fichier php est appelé via le Web et non définie si elle est exécutée par la ligne de commande.

Sur mon serveur Web, je teste si la variable d'environnement Apache_RUN_DIR est définie comme ceci:

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

Pour vous assurer qu'il fonctionnera sur votre serveur Web, vous pouvez mettre un fichier php factice sur votre serveur Web avec cette seule déclaration:

<?php var_dump($_ENV);  ?>

Ensuite 1) chargez-le avec votre navigateur Web et 2) chargez-le à partir de la ligne de commande comme ceci

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

Comparez les différences et testez la variable appropriée.

2
eosphere

Dans la commande cron, ajoutez ?source=cron à la fin du chemin du script. Ensuite, dans votre script, cochez $_GET['source'].

EDIT: désolé, c'est un script Shell donc ne peut pas utiliser qs. Vous pouvez, je pense, passer des arguments sous la forme php script.php arg1 arg2 puis lisez-les avec $argv.

1
Adam Hopkinson

$_SERVER['SESSIONNAME'] contient Console s'il est exécuté à partir de la CLI. Peut-être que cela aide.

1
Bouke

C'est très simple. Les démons Cron exportent toujours la variable d'environnement MAILTO. Vérifiez si elle existe et a une valeur non vide - puis vous exécutez à partir de cron.

0
Gabor Garami

posix_isatty(STDOUT) return FALSE si la sortie de l'appel cli est redirigée (pipe ou fichier) ...

0
Daniel
if(!$_SERVER['HTTP_Host']) {
 blabla();
}
0
Ivor

Sur mon serveur Amazon Linux, c'est ce qui a fonctionné pour moi:

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

Le répertoire personnel est défini sur le vôtre si vous l'exécutez. Si vous utilisez Sudo pour l'exécuter, le répertoire personnel est défini sur/root.

0
Phil Marshall

Je pense qu'il serait préférable d'exécuter la commande cron avec une option supplémentaire sur la ligne de commande que vous n'exécuteriez pas manuellement.

cron ferait:

command ext_updates=1

manuel ferait:

command 

Ajoutez simplement une option dans le script lui-même pour que le paramètre ext_updates ait la valeur par défaut false.

0
Christopher Rivera