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

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

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

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

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

Есть идеи?

Добавление в качестве связанного вопроса: Определить, запускается ли скрипт PHP в интерактивном режиме или нет

Leigh 04.07.2012 15:30

@Leigh: Спасибо за ссылку!

hakre 06.11.2015 14:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
60
2
39 247
21
Перейти к ответу Данный вопрос помечен как решенный

Ответы 21

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

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

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

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

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

Matthew Scharley 10.10.2008 14:50

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

CRON=running

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

Я никогда не знал, что вы можете поместить другие команды / строки переменных в crontab таким образом. я только когда-либо изучал синтаксис для временных команд. это плюс php $ _ENV и $ _SERVER поможет в том, чего я пытаюсь достичь. Благодарность

Uberfuzzy 10.10.2008 19:44

"man 5 crontab" для всех мельчайших деталей. Но да, вы можете установить переменные среды. Обычно это используется для установки PATH или, возможно, SHELL, но вы можете установить все, что захотите.

Matthew Scharley 11.10.2008 01:48

Просто хотел отметить, что variables_order должен включать 'E' в php.ini, чтобы массив $_ENV был заполнен

andrewtweber 13.04.2012 01:19

@andrewtweber: вы все равно сможете использовать getenv(), если только вы не заблокированы безопасным режимом.

Matthew Scharley 13.04.2012 03:00

Это хитрый трюк - просто успешно использовал, спасибо.

Eric 12.01.2017 15:45

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

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

Предположительно, вы бы настроили его таким образом, чтобы «отсутствие параметра» означало, что вы работаете в интерактивном режиме, чтобы вы не могли его забыть. Но интересное решение.

Matthew Scharley 10.10.2008 15:24

Да, очень интересный подход. Могут быть использованы и другие вещи, не относящиеся к PHP.

Uberfuzzy 10.10.2008 17:48
Ответ принят как подходящий

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

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

  • СРОК
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

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

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

после изучения это кажется лучшим методом. между _ENV + _SERVER, я уверен, что могу с уверенностью определить, кто / где / кто его запускает, и действовать соответственно. бегущая строка CRON = в crontab тоже поможет.

Uberfuzzy 10.10.2008 18:15

Кажется, что php.ini по умолчанию со временем изменился, чтобы НЕ заполнять глобальный $ _ENV по умолчанию. Безопасный способ - использовать getenv ('SSH_CLIENT') - см. Здесь для получения дополнительной информации stackoverflow.com/questions/3780866/why-is-my-env-empty

GuruBob 17.09.2015 23:53

$_SERVER['SESSIONNAME'] содержит Console при запуске из интерфейса командной строки. Может, это поможет.

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

Sean McSomething 23.01.2009 02:54

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

Для меня это наиболее разумно, за исключением того, что я бы использовал if (! Isset ($ _ SERVER ['HTTP_HOST'])) {blah (); }.

Stacey Richards 14.12.2009 01:53

Я почти уверен, что $ _SERVER ['HTTP_HOST'] не присутствует при запуске обоих сценариев Cron- ИЛИ CLI. Исходный плакат - это способ различать Cron и CLI, а не CLI и Интернет.

Rob Howard 21.05.2010 03:55

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

cron сделает:

command ext_updates=1

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

command 

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

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

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

Спасибо, это помогло мне со стихами веб-браузера Командная строка

Phill Pafford 12.11.2010 21:42

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

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

Это то, что я искал, хотя, запустив это в своей системе (и в TTY), я получаю предупреждение: PHP Warning: posix_isatty(): cannot seek on a pipe in /root/test.php on line 3, поэтому я использовал @ перед вызовом, чтобы отключить предупреждение. Также обратите внимание, что этот метод будет обнаруживать вызовы в командной строке, которые передаются на что-то еще, как «не tty» - это в основном то, что я хотел, но YMMV.

Guss 22.08.2011 15:39

@Guss: Поздно, но причина, по которой каналы не регистрируются как TTY, заключается в том, что ваш STDOUT не является TTY, если вас перенаправляют на что-то другое; ваш STDOUT - это STDIN другого приложения, а не TTY, под которым вы его запускаете. Я считаю, что последнее приложение в трубе по-прежнему будет видеть TTY на своем STDOUT.

Matthew Scharley 19.07.2012 04:17

Да, вы правы, и это соответствует тому поведению, которое я ожидаю получить. Я хочу посмотреть, есть ли это TTY, чтобы я мог манипулировать экраном, чего я, очевидно, не могу сделать на канале :-)

Guss 20.07.2012 03:35

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

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

Жутко. Пытаться

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

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

Вот что я использую, чтобы узнать, откуда выполняется сценарий. Посмотрите функцию 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 (может быть cli или cli_server), мы проверяем, пуст ли $_SERVER['REMOTE_ADDR']. При вызове из командной строки он должен быть пустым.

Из комментариев: Обратите внимание, что двоичный файл php-cgi можно вызвать из командной строки, из сценария оболочки или как задание cron! Если это так, php_sapi_name () всегда будет возвращать одно и то же значение (т.е. «cgi-fcgi») вместо «cli», которое вы могли ожидать.

pierdevara 11.05.2015 19:58

@pierdevara Я этого не знал! Я соответствующим образом отредактирую свой пост. Спасибо что подметил это.

davethebrave 14.05.2015 15:46

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

Спасибо, это помогло.

Paras Shah 16.09.2017 16:22

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

Лучший ответ здесь! Я просто определил его в одной из своих констант: define( 'PHP_SAPI', ( php_sapi_name() == 'cli' ) ? ( isset( $_SERVER['TERM'] ) ? 1 : 2 ) : 0 );.

Ilia Rostovtsev 27.04.2014 17:45

Это определенно лучшее. Отвечает на все возможные вопросы. (Лично я пришел сюда, чтобы узнать, что такое web vs. cron, так что это здорово!)

JMTyler 08.07.2014 17:00

Я не уверен, что что-то изменилось за последние несколько лет, но в моей системе (CentOS 6.6, PHP 5.4.38, работает Litespeed) php_sapi_name() возвращает cgi-fcgi при запуске через cron. Другими словами, если ваша система похожа на мою, вы можете заменить первую строку этого кода ответа на: if (php_sapi_name() == 'cli' || php_sapi_name() == 'cgi-fcgi') {.

rinogo 05.03.2015 20:42

Windows не имеет $_SERVER['term']

Brad Kent 13.04.2017 07:08

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

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

Gabor Garami 04.03.2013 11:32
как правило MAILTO is set in the crontab, but it can as well be not. just stuck in the shared hosting setup which proves that. so for public domain scripts best method is to check for either term vars or use forcibly added environment variable as @agi suggested
shomeax 06.03.2013 23:12

getenv('TERM')

Набивка для SO 30 char мин.

Другой вариант - проверить конкретную переменную среды, которая устанавливается, когда файл 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

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

На моем сервере Amazon Linux это сработало для меня:

$ isCron = ($ _SERVER ['ДОМОЙ'] == '/');

Домашний каталог станет вашим, если вы просто запустите его. Если вы используете sudo для его запуска, домашний каталог будет / root.

Другие вопросы по теме