2008-10-10 16 views
47

Estoy buscando la manera de PHP para detectar si un script se ejecutó desde una invocación manual en un shell (yo iniciando sesión y ejecutándolo), o si se ejecutó desde la entrada crontab.¿Puede PHP detectar si se ejecuta desde un trabajo cron o desde la línea de comandos?

Tengo varias escrituras de tipo de mantenimiento escritas en php que he configurado para ejecutar en mi crontab. Ocasionalmente, y debo ejecutarlos manualmente antes de lo previsto o si algo falla/se rompe, necesito ejecutarlos un par de veces.

El problema con esto es que también tengo algunas notificaciones externas establecidas en las tareas (publicar en Twitter, enviar un correo electrónico, etc.) que NO QUIERO que ocurra cada vez que ejecuto el script manualmente.

Estoy usando php5 (si es importante), es un entorno de servidor linux bastante estándar.

¿Alguna idea?

+2

Agregando como una pregunta vinculada: [Detecta si un script PHP se está ejecutando interactivamente o no] (http://stackoverflow.com/questions/11327367/detect-if-a-php-script-is-being-run -interactively-or-not/11327451) – Leigh

+0

@Leigh: ¡gracias por la referencia! – hakre

Respuesta

39

En lugar de detectar cuando el script se ejecuta desde el crontab, es probablemente más fácil de detectar cuando se está ejecutando de forma manual.

Hay una gran cantidad de variables de entorno (en el array $ _ENV) que se establece cuando se ejecuta un script desde la línea de comandos. Cuáles son éstos puede variar en función de la configuración Sever y cómo entrar en mi entorno, se establecen las siguientes variables de entorno cuando se ejecuta un script manualmente que no están presentes cuando se ejecuta desde cron:.

  • PLAZO
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

hay otros también. Así, por ejemplo si siempre utiliza SSH para acceder al cuadro, a continuación, la siguiente línea detectaría si el script se ejecuta desde cron:

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

+1

después de mirarlo, este parece ser el mejor método. entre _ENV + _SERVER, estoy bastante seguro de que puedo detectar con confianza quién/dónde/quién lo está ejecutando, y actuar en consecuencia. la línea CRON = corriendo en crontab también ayudará. – Uberfuzzy

+1

Parece que el php.ini predeterminado ha cambiado con el tiempo para NO llenar el $ _ENV global de forma predeterminada. Una forma segura es usar getenv ('SSH_CLIENT') - ver aquí para más información http://stackoverflow.com/questions/3780866/why-is-my-env-empty – GuruBob

5

No es que yo sepa, probablemente la solución más simple es proporcionar un parámetro extra para decirle al script cómo se invocó.

4

Me gustaría ver en $_ENV (var_dump()) y comprobar si nota una diferencia cuando lo ejecuta frente a cuando el cronjob lo ejecuta. Aparte de eso, no creo que haya un cambio "oficial" que te diga lo que sucedió.

2

En el comando cron, agregue ?source=cron al final de la ruta del script. Luego, en su secuencia de comandos, marque $_GET['source'].

EDITAR: lo siento, es un script de shell por lo que no puede usar qs. Puede, creo, pasar argumentos en el formulario php script.php arg1 arg2 y luego leerlos con .

+2

Es un script de shell, no una página web. –

27

Usted puede configurar un parámetro extra, o añadir una línea en su crontab, tal vez:

CRON=running 

Y entonces usted puede comprobar las variables de entorno para "CRON". Además, intente comprobar la variable $ SHELL, no estoy seguro de si/qué cron lo establece.

+0

nunca pensé que pudiera poner otros comandos/líneas de variables en el crontab así. Solo aprendí la sintaxis de los comandos temporizados. esto más el php $ _ENV y $ _SERVER, ayudarán a lo que estoy tratando de lograr. gracias – Uberfuzzy

+0

"man 5 crontab" por todos los detalles arenosos. Pero sí, puedes establecer variables de entorno. Normalmente esto se usa para establecer PATH o quizás SHELL, pero puede establecer lo que quiera. –

+1

Solo quería señalar que 'variables_order' debe incluir 'E' en php.ini para que se llene la matriz '$ _ENV' – andrewtweber

6

No sé específicamente acerca de PHP, pero puede caminar por el árbol de procesos hasta que encuentre init o cron.

Suponiendo PHP puede conseguir su propio identificador de proceso y ejecutar comandos externos, debería ser una cuestión de ejecutar ps -ef | grep piddonde pid es su propio identificador de proceso y extraer el ID del proceso padre (PPID) de la misma.

Luego haz lo mismo con ese PPID hasta llegar a cron como padre o init como padre.

Por ejemplo, este es mi árbol de procesos y se puede ver la cadena de propiedad, 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 

Los mismos procesos se ejecutan bajo cron se vería así:

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 

Esta solución de "caminar por el árbol de procesos" significa que no tiene que preocuparse de introducir un parámetro artificial para indicar si se está ejecutando bajo cron o no; puede olvidarse de hacerlo en su sesión interactiva y cosas cosas.

+0

Presumiblemente, lo configuraría de tal manera que "sin parámetros" significa que está ejecutando interactivamente, de esa manera no podría olvidarlo. Solución interesante sin embargo. –

+0

Sí, enfoque muy interesante. Podría tener usos para otras cosas que no sean php también. – Uberfuzzy

1

$_SERVER['SESSIONNAME'] contiene Console si se ejecuta desde la línea de comandos. Tal vez eso ayude.

+3

Usaría php_sapi_name() - pero eso aún no diferenciará entre cron y la ejecución CLI interactiva. –

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

Esto tiene más sentido para mí, excepto que usaría if (! Isset ($ _ SERVER ['HTTP_HOST'])) {blah(); }. –

+1

Estoy bastante seguro de que $ _SERVER ['HTTP_HOST'] no está presente cuando se ejecutan scripts de ejecución de Cron o CLI. El póster original busca una forma de diferenciar entre Cron y CLI, no CLI y Web. –

0

Creo que sería mejor ejecutar el comando cron con una opción adicional en la línea de comandos que no se ejecutaría manualmente.

cron haría:

command ext_updates=1 

Manual haría:

command 

Sólo añadir una opción en el propio guión para que el parámetro ext_updates tener un valor por defecto de la falsa.

3

En mi entorno, encontré que TERM se configuró en $_SERVER si se ejecuta desde la línea de comandos, pero no se establece si se ejecuta mediante Apache como una solicitud web. Pongo esto en la parte superior de mi script que podría ejecutar desde la línea de comandos, o podría tener acceso mediante un navegador web:

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

Gracias esto me ayudó con los versos del navegador web Línea de comandos –

14

El enfoque correcto es utilizar la función posix_isatty() en, por ejemplo, el descriptor de archivo de salida estándar, así:

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

Esto es lo que estaba buscando, aunque ejecutando esto en mi sistema (y en un TTY), aparece una advertencia: 'PHP Advertencia: posix_isatty(): no puedo buscar una tubería en /root/test.php en la línea 3', así que usé el '@' delante de la llamada para silenciar la advertencia. También tenga en cuenta que este método detectará las llamadas en la línea de comandos que están conectadas a otra cosa como "no a tty", que es básicamente lo que yo quería, pero YMMV. – Guss

+0

@Guss: tarde, pero el motivo por el que las canalizaciones no se registran como TTY es porque su STDOUT no es un TTY si se lo canaliza hacia otra cosa; su STDOUT es otra de las aplicaciones STDIN, no el TTY con el que lo está ejecutando. Creo que la última aplicación en la tubería aún verá un TTY en su STDOUT. –

+0

Sí, tiene razón, y esto es consistente con el comportamiento que espero obtener. Quiero ver si es un TTY, así que puedo hacer algo de manipulación de la pantalla, lo que obviamente no puedo hacer en una tubería :-) – Guss

0

posix_isatty(STDOUT) return FALSE si se redirige la salida de la llamada CLI (tubería o archivo) ...

-4

Es fácil para mí ... Sólo count($_SERVER['argc']) y si usted tiene un resultado mayor que cero se estará quedando sin servidor. Solo necesita agregar a su $_SERVER['argv'] su variable personalizada, como "CronJob"=true;

3

Creepy. Pruebe

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

en su lugar. PHP Client Binary no lo envía. El tipo de término simplemente funciona cuando PHP se utiliza como módulo (es decir, apache), pero cuando se ejecuta php a través de la interfaz CGI, utilice el ejemplo anterior.

24

Esto es lo que uso para descubrir dónde se ejecuta el script. Mira la función php_sapi_name para más información: 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() no incluye cli (podría ser cli o cli_server) a continuación, comprobamos si $_SERVER['REMOTE_ADDR'] está vacía. Cuando se llama desde la línea de comando, esto debería estar vacío.

+1

De los comentarios: Tenga en cuenta que el binario php-cgi se puede llamar desde la línea de comandos, desde un script de shell o como un trabajo cron también! Si es así, php_sapi_name() siempre devolverá el mismo valor (es decir, "cgi-fcgi") en lugar de "cli", que podría esperar. – pierdevara

+0

@pierdevara ¡No lo sabía! Editaré mi publicación en consecuencia. Gracias por señalar eso. – davethebrave

14

Creo que la solución más universal es agregar una variable de entorno al comando cron, y buscarla en el código. Funcionará en todos los sistemas.

Si el comando ejecutado por el cron es, por ejemplo:

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

cambiarlo a

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

A continuación, se puede comprobar en el código:

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

Gracias, esto ayudó. –

24
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"; 
} 
+0

¡La mejor respuesta aquí! Simplemente lo definí en una de mis constantes: 'define ('PHP_SAPI', (php_sapi_name() == 'cli')? (Isset ($ _SERVER ['TERM'])? 1: 2): 0);' –

+0

Este es definitivamente el mejor. Responde todas las preguntas posibles. (Personalmente, vine aquí preguntándome sobre web vs. cron, ¡así que esto es genial!) – JMTyler

+1

No estoy seguro de si algo ha cambiado en los últimos años, pero en mi sistema (CentOS 6.6, PHP 5.4.38, ejecutando Litespeed), 'php_sapi_name()' devuelve 'cgi-fcgi' cuando se ejecuta a través de cron. En otras palabras, si su sistema es como el mío, puede cambiar la primera línea del código de esta respuesta con '' if (php_sapi_name() == 'cli' || php_sapi_name() == 'cgi-fcgi') {'. – rinogo

0

Esto es muy fácil. Cron Daemons siempre exporta MAILTO variable de entorno. Verifique si existe y tiene un valor no vacío; luego, ejecute desde cron.

+0

eso simplemente no es verdad :) – shomeax

+0

@shomeax ¿por qué crees que no es cierto? No es un método peor que los anteriores, y _normalmente_ MAILTO tiene algún valor (incluso si es igual a USUARIO), porque la salida siempre se envía por correo electrónico. –

+0

* normalmente * MAILTO se establece en el crontab, pero bien puede ser que no. simplemente atrapado en la configuración de alojamiento compartido que lo demuestra. por lo tanto, para las secuencias de comandos de dominio público, el mejor método es comprobar si hay variables de términos o usar una variable de entorno añadida a la fuerza, como sugirió @agi – shomeax

2
getenv('TERM') 

Relleno para SO's 30 char min.

1

Otra opción sería probar una variable de entorno específica que se establece cuando el archivo php se invoca a través de la web y no se establece si se ejecuta por la línea de comandos.

En mi servidor web que estoy probando si la variable de entorno APACHE_RUN_DIR se establece así:

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

Para asegurarse de que funcione en su servidor web, se puede poner un archivo PHP maniquí en su Web servidor con esta sola declaración:

<?php var_dump($_ENV); ?> 

Entonces 1) cargarlo con su navegador web y 2) cargarlo desde la línea de comandos como ésta

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

Compara las diferencias y prueba la variable adecuada.

Cuestiones relacionadas