Может ли PHP определить, запускается ли он из задания cron или из командной строки?

44

Я ищу путь к PHP, чтобы определить, был ли запущен script из ручного вызова в оболочке (я вхожу в систему и запускаю его), или если он был запущен из записи crontab.

У меня есть различные сценарии типа обслуживания, написанные на php, которые я установил для запуска в моем crontab. Иногда, и мне нужно запускать их вручную досрочно или если что-то не получилось/сломано, мне нужно запустить их пару раз.

Проблема заключается в том, что у меня также есть некоторые внешние уведомления, заданные в задачах (отправка в твиттер, отправка электронной почты и т.д.), которые я НЕ хочу делать каждый раз, когда я запускаю script вручную.

Я использую php5 (если это имеет значение), его довольно стандартная среда сервера linux.

Любые идеи?

Теги:
cron

20 ответов

38
Лучший ответ

Вместо обнаружения, когда script запускается из crontab, его, вероятно, легче обнаружить, когда вы запускаете его вручную.

Существует множество переменных среды (в массиве $_ENV), которые устанавливаются при запуске script из командной строки. Что это будет зависеть от настройки вашего сервера и того, как вы входите в систему. В моей среде следующие переменные среды устанавливаются при запуске script вручную, которые отсутствуют при запуске из cron:

  • TERM
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

Есть и другие. Так, например, если вы всегда используете SSH для доступа к этому полю, тогда следующая строка будет определять, выполняется ли script из cron:

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

  • 1
    после рассмотрения, это кажется лучшим методом. между _ENV + _SERVER, я почти уверен, что могу с уверенностью определить, кто / где / кто его запускает, и действовать соответственно. CRON = бегущая строка в crontab тоже поможет.
  • 2
    Похоже, что php.ini по умолчанию изменился с течением времени, чтобы НЕ заполнять глобальный $ _ENV по умолчанию. Безопасный способ - использовать getenv ('SSH_CLIENT') - для получения дополнительной информации см. Здесь stackoverflow.com/questions/3780866/why-is-my-env-empty
26

Вы можете настроить дополнительный параметр или добавить строку в свой crontab, возможно:

CRON=running

И затем вы можете проверить переменные среды для "CRON". Кроме того, попробуйте проверить переменную $SHELL, я не уверен, что /cron устанавливает ее.

  • 0
    Я никогда не знал, что вы можете поместить другие команды / строки переменных в crontab, как это. я только когда-либо узнал синтаксис для синхронизированных команд. это плюс php $ _ENV и $ _SERVER, поможет мне достичь того, чего я пытаюсь достичь. Спасибо
  • 0
    "man 5 crontab" за все мелкие детали. Но да, вы можете установить переменные среды. Обычно это используется для установки PATH или, возможно, SHELL, но вы можете установить все что угодно.
Показать ещё 4 комментария
24

Здесь я использую, чтобы узнать, где выполняется script. Посмотрите на функцию 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";
}

EDIT: Если php_sapi_name() не включает cli (может быть cli или cli_server), тогда мы проверяем, является ли $_SERVER['REMOTE_ADDR'] пустым. При вызове из командной строки это должно быть пустым.

  • 1
    Из комментариев: Обратите внимание, что двоичный файл php-cgi может быть вызван из командной строки, из сценария оболочки или также как задание cron! Если это так, php_sapi_name () всегда будет возвращать одно и то же значение (то есть «cgi-fcgi») вместо «cli», которое вы могли ожидать.
  • 0
    @pierdevara Я не знал этого! Я буду редактировать свой пост соответственно. Спасибо что подметил это.
23
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";   
}
  • 1
    Лучший ответ здесь! Я просто определил его в одну из моих констант: define( 'PHP_SAPI', ( php_sapi_name() == 'cli' ) ? ( isset( $_SERVER['TERM'] ) ? 1 : 2 ) : 0 );
  • 0
    Это определенно лучший. Отвечает на все возможные вопросы. (Лично я пришел сюда, задаваясь вопросом о веб против cron, так что это здорово!)
Показать ещё 2 комментария
14

Правильный подход заключается в использовании функции posix_isatty(), например. дескриптор файла stdout, например:

if (posix_isatty(STDOUT))
    /* do interactive terminal stuff here */
  • 0
    Это то, что я искал, хотя, запустив это в моей системе (и в TTY), я получаю предупреждение: PHP Warning: posix_isatty(): cannot seek on a pipe in /root/test.php on line 3 , поэтому Я использовал @ перед звонком, чтобы заставить замолчать предупреждение. Также обратите внимание, что этот метод будет обнаруживать вызовы в командной строке, которые передаются на что-то еще, как «не tty» - что в основном то, что я хотел, но YMMV.
  • 0
    @Guss: Поздно, но причина, по которой каналы не регистрируются в качестве TTY, заключается в том, что ваш STDOUT не является TTY, если вас направляют на что-то еще; ваш STDOUT - это другой STDIN для приложений, а не TTY, под которым вы его запускаете. Я верю, что последнее приложение в трубе все еще увидит TTY на своем STDOUT.
Показать ещё 1 комментарий
13

Я думаю, что наиболее универсальным решением является добавление переменной среды в команду 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";
  • 0
    Спасибо, это помогло.
6

Я не знаю о PHP специально, но вы можете подойти к дереву процессов, пока не найдете либо init, либо cron.

Предполагая, что PHP может получить свой собственный идентификатор процесса и запустить внешние команды, необходимо выполнить ps -ef | grep pid, где pid - ваш собственный идентификатор процесса и извлечение идентификатора родительского процесса (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 или нет - вы можете забыть сделать это в своем интерактивном сеансе и в других вещах вверх.

  • 0
    Предположительно, вы бы настроили его так, чтобы «без параметров» означало, что вы работаете в интерактивном режиме, и вы не могли бы забыть об этом. Интересное решение, хотя.
  • 0
    Да, очень интересный подход. Может также использоваться для других не-php вещей.
5

Не то, что я знаю - возможно, самым простым решением является предоставление дополнительного параметра самому, чтобы сообщить script, как он был вызван.

4

Я бы посмотрел на $_ENV (var_dump() it) и посмотрел, заметили ли вы разницу, когда вы запускаете его, и когда запускается cronjob. Помимо этого, я не думаю, что есть "официальный" переключатель, который сообщает вам, что произошло.

3

Жуткие. Попробуйте

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

вместо этого. PHP Client Binary не отправляет его. Тип Term работает только тогда, когда PHP используется как модуль (например, apache), но при запуске php через интерфейс CGI используйте пример выше!

3

В моей среде я обнаружил, что TERM был установлен в $_SERVER, если он запущен из командной строки, но не установлен, если выполняется через Apache в качестве веб-запроса. Я поставил это в верхней части моего script, который я мог бы запустить из командной строки, или может получить доступ через веб-браузер:

if (isset($_SERVER{'TERM'}))
{
    class::doStuffShell();
}
else
{
    class::doStuffWeb();
}
  • 1
    Спасибо, это помогло мне с веб-браузером стихи командной строки
2
getenv('TERM')

Заполнение для SO 30 char мин.

2

В команде cron добавьте ?source=cron в конец пути script. Затем в script выберите $_GET['source'].

EDIT: извините, это оболочка script, поэтому не может использовать qs. Вы можете, я думаю, передать аргументы в форме PHP скрипт.php arg1 arg2, а затем прочитать их с помощью $argv.

  • 2
    Это сценарий оболочки, а не веб-страница.
1

Другой вариант - проверить определенную переменную среды, которая устанавливается, когда php файл вызывается через Интернет и не устанавливается, если он запускается с помощью командной строки.

На моем веб-сервере я тестирую, если переменная среды APACHE_RUN_DIR установлена ​​следующим образом:

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

Чтобы убедиться, что он будет работать на вашем веб-сервере, вы можете поместить фиктивный php файл на свой веб-сервер с помощью этого единственного оператора:

<?php var_dump($_ENV);  ?>

Затем 1) загрузите его в свой веб-браузер и 2) загрузите его из командной строки следующим образом

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

Сравните различия и проверьте соответствующую переменную.

1

$_SERVER['SESSIONNAME'] содержит Console, если выполняется из CLI. Возможно, это помогает.

  • 3
    Я бы использовал php_sapi_name () - но это все равно не будет отличать выполнение cron от интерактивного CLI.
0

Это очень просто. Cron Daemons всегда экспортируют переменную среды MAILTO. Проверьте, существует ли он и имеет непустое значение - тогда вы работаете из cron.

  • 0
    это просто неправда :)
  • 0
    @shomeax, почему вы думаете, что это не так? Не хуже метода, чем предыдущие, и обычно MAILTO имеет некоторое значение (даже если оно равно USER), потому что вывод всегда доставляется по электронной почте.
Показать ещё 1 комментарий
0

posix_isatty(STDOUT) return FALSE, если вывод вызова cli перенаправляется (канал или файл)...

0

Я думаю, было бы лучше запустить cron commmand с дополнительной опцией в командной строке, которую вы не запускали вручную.

cron сделает:

command ext_updates=1

руководство будет делать:

command 

Просто добавьте параметр в script, чтобы параметр ext_updates имел значение false по умолчанию.

0
if(!$_SERVER['HTTP_HOST']) {
 blabla();
}
  • 0
    Это наиболее разумно для меня, за исключением того, что я бы использовал if (! Isset ($ _ SERVER ['HTTP_HOST'])) {blah (); }.
  • 1
    Я почти уверен, что $ _SERVER ['HTTP_HOST'] не присутствует при запуске обоих скриптов, запускаемых Cron или CLI. Оригинальный постер позволяет различать Cron и CLI, а не CLI и Web.
-5

Это легко для меня... Просто count($_SERVER['argc']), и если у вас есть результат выше нуля, он будет работать на сервере. Вам просто нужно добавить в свою $_SERVER['argv'] свою настраиваемую переменную, например "CronJob"=true;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню