Я ищу способ PHP, чтобы определить, был ли скрипт запущен из ручного вызова в оболочке (я вхожу в систему и запускаю его), или если он был запущен из записи crontab.
У меня есть сценарии различных типов обслуживания, написанные на php, которые я установил для запуска в моем crontab. Иногда мне нужно запускать их вручную с опережением графика или, если что-то выходит из строя / ломается, мне нужно запускать их пару раз.
Проблема в том, что у меня также есть некоторые внешние уведомления, установленные в задачах (публикация в твиттере, отправка электронной почты и т. д.), Которые я НЕ хочу выполнять каждый раз, когда я запускаю скрипт вручную.
Я использую php5 (если это имеет значение), это довольно стандартная серверная среда Linux.
Есть идеи?
@Leigh: Спасибо за ссылку!






Насколько мне известно, возможно, самое простое решение - предоставить дополнительный параметр самому сценарию, чтобы сообщить скрипту, как он был вызван.
Я бы посмотрел на $_ENV (var_dump () it) и проверил, заметили ли вы разницу, когда вы запускаете его, и когда его запускает cronjob. Кроме того, я не думаю, что есть «официальный» переключатель, который сообщает вам, что произошло.
В команде cron добавьте ?source=cron в конец пути скрипта. Затем в своем скрипте проверьте $_GET['source'].
Обновлено: извините, это сценарий оболочки, поэтому нельзя использовать qs. Я думаю, вы можете передать аргументы в форме php script.php arg1 arg2, а затем прочитать их с помощью $argv.
Это сценарий оболочки, а не веб-страница.
Вы можете настроить дополнительный параметр или добавить строку в свой crontab, например:
CRON=running
А затем вы можете проверить переменные среды на наличие «CRON». Кроме того, попробуйте проверить переменную $ SHELL, я не уверен, что / что это устанавливает cron.
Я никогда не знал, что вы можете поместить другие команды / строки переменных в crontab таким образом. я только когда-либо изучал синтаксис для временных команд. это плюс php $ _ENV и $ _SERVER поможет в том, чего я пытаюсь достичь. Благодарность
"man 5 crontab" для всех мельчайших деталей. Но да, вы можете установить переменные среды. Обычно это используется для установки PATH или, возможно, SHELL, но вы можете установить все, что захотите.
Просто хотел отметить, что variables_order должен включать 'E' в php.ini, чтобы массив $_ENV был заполнен
@andrewtweber: вы все равно сможете использовать getenv(), если только вы не заблокированы безопасным режимом.
Это хитрый трюк - просто успешно использовал, спасибо.
Я не знаю конкретно о 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 или нет - вы можете забыть сделать это в интерактивном сеансе и все усложнить.
Предположительно, вы бы настроили его таким образом, чтобы «отсутствие параметра» означало, что вы работаете в интерактивном режиме, чтобы вы не могли его забыть. Но интересное решение.
Да, очень интересный подход. Могут быть использованы и другие вещи, не относящиеся к PHP.
Вместо того, чтобы определять, когда сценарий запускается из crontab, вероятно, легче обнаружить, когда вы запускаете его вручную.
Существует множество переменных среды (в массиве $ _ENV), которые устанавливаются при запуске сценария из командной строки. То, что они собой представляют, будет зависеть от настроек вашего сервера и способа входа в систему. В моей среде при запуске скрипта вручную устанавливаются следующие переменные среды, которых нет при запуске из cron:
Есть и другие. Так, например, если вы всегда используете SSH для доступа к ящику, следующая строка определит, запущен ли скрипт из cron:
$cron = !isset($_ENV['SSH_CLIENT']);
после изучения это кажется лучшим методом. между _ENV + _SERVER, я уверен, что могу с уверенностью определить, кто / где / кто его запускает, и действовать соответственно. бегущая строка CRON = в crontab тоже поможет.
Кажется, что php.ini по умолчанию со временем изменился, чтобы НЕ заполнять глобальный $ _ENV по умолчанию. Безопасный способ - использовать getenv ('SSH_CLIENT') - см. Здесь для получения дополнительной информации stackoverflow.com/questions/3780866/why-is-my-env-empty
$_SERVER['SESSIONNAME'] содержит Console при запуске из интерфейса командной строки. Может, это поможет.
Я бы использовал php_sapi_name (), но это по-прежнему не будет различать cron и интерактивное выполнение CLI.
if (!$_SERVER['HTTP_HOST']) {
blabla();
}
Для меня это наиболее разумно, за исключением того, что я бы использовал if (! Isset ($ _ SERVER ['HTTP_HOST'])) {blah (); }.
Я почти уверен, что $ _SERVER ['HTTP_HOST'] не присутствует при запуске обоих сценариев Cron- ИЛИ CLI. Исходный плакат - это способ различать Cron и CLI, а не CLI и Интернет.
Я думаю, что было бы лучше запустить команду cron с дополнительной опцией в командной строке, которую вы не запускали бы вручную.
cron сделает:
command ext_updates=1
руководство сделало бы:
command
Просто добавьте параметр в сам скрипт, чтобы параметр ext_updates имел значение по умолчанию false.
В моей среде я обнаружил, что TERM был установлен в $_SERVER при запуске из командной строки, но не установлен при запуске через Apache в качестве веб-запроса. Я помещаю это в начало своего сценария, который я могу запустить из командной строки или получить доступ через веб-браузер:
if (isset($_SERVER{'TERM'}))
{
class::doStuffShell();
}
else
{
class::doStuffWeb();
}
Спасибо, это помогло мне со стихами веб-браузера Командная строка
Правильный подход - использовать функцию 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: Поздно, но причина, по которой каналы не регистрируются как TTY, заключается в том, что ваш STDOUT не является TTY, если вас перенаправляют на что-то другое; ваш STDOUT - это STDIN другого приложения, а не TTY, под которым вы его запускаете. Я считаю, что последнее приложение в трубе по-прежнему будет видеть TTY на своем STDOUT.
Да, вы правы, и это соответствует тому поведению, которое я ожидаю получить. Я хочу посмотреть, есть ли это TTY, чтобы я мог манипулировать экраном, чего я, очевидно, не могу сделать на канале :-)
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 Я этого не знал! Я соответствующим образом отредактирую свой пост. Спасибо что подметил это.
Я считаю, что наиболее универсальным решением является добавление переменной окружения в команду 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";
Спасибо, это помогло.
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 );.
Это определенно лучшее. Отвечает на все возможные вопросы. (Лично я пришел сюда, чтобы узнать, что такое web vs. cron, так что это здорово!)
Я не уверен, что что-то изменилось за последние несколько лет, но в моей системе (CentOS 6.6, PHP 5.4.38, работает Litespeed) php_sapi_name() возвращает cgi-fcgi при запуске через cron. Другими словами, если ваша система похожа на мою, вы можете заменить первую строку этого кода ответа на: if (php_sapi_name() == 'cli' || php_sapi_name() == 'cgi-fcgi') {.
Windows не имеет $_SERVER['term']
Это очень просто. Демоны Cron всегда экспортируют переменную среды MAILTO. Проверьте, существует ли он и имеет ли непустое значение - тогда вы работаете из cron.
@shomeax, почему ты думаешь, что это неправда? Не худший метод, чем предыдущие, и как правило MAILTO имеет некоторое значение (даже если оно равно USER), потому что вывод всегда доставляется по электронной почте.
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.
Добавление в качестве связанного вопроса: Определить, запускается ли скрипт PHP в интерактивном режиме или нет