Может ли PHP определить, выполняется ли он из задания cron или из командной строки?
Я ищу способ PHP, чтобы определить, был ли скрипт запущен из ручного вызова в оболочке (я вошел в систему и запустил его), или он был запущен из записи crontab.
У меня есть различные сценарии типа обслуживания, написанные на php, которые я настроил для запуска в своей crontab. Иногда, и мне нужно запускать их вручную с опережением графика или, если что-то не удалось/сломалось, мне нужно запустить их пару раз.
Проблема в том, что у меня также установлены некоторые внешние уведомления в задачи (публикация в Twitter, отправка электронной почты и т. Д.), Которые я НЕ хочу выполнять каждый раз, когда запускаю скрипт вручную.
Я использую php5 (если это имеет значение), это довольно стандартная серверная среда Linux.
Есть идеи?
20 answers
Вместо того, чтобы определять, когда скрипт запускается с кронтаба, вероятно, проще определить, когда вы запускаете его вручную.
Существует множество переменных среды (в массиве $_ENV), которые устанавливаются при запуске сценария из командной строки. Что это такое, будет зависеть от настроек вашего сервера и способа входа в систему. В моей среде при запуске сценария вручную устанавливаются следующие переменные среды, которые отсутствуют при запуске из крон:
- ТЕРМИН
- SSH_CLIENT
- SSH_TTY
- SSH_СОЕДИНЕНИЕ
Есть и другие. Так, например, если вы всегда используете SSH для доступа к ящику, то следующая строка определит, выполняется ли скрипт из cron:
$cron = !isset($_ENV['SSH_CLIENT']);
Вы можете настроить дополнительный параметр или добавить строку в свой кронтаб, например:
CRON=running
А затем вы можете проверить свои переменные среды на наличие "CRON". Кроме того, попробуйте проверить переменную $SHELL, я не уверен, что/какой cron устанавливает ее значение.
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";
}
Вот что я использую, чтобы узнать, откуда выполняется скрипт. Посмотрите на функцию 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']
пустым. При вызове из командной строки это поле должно быть пустым.
Я думаю, что наиболее универсальным решением является добавление переменной среды в команду 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";
Правильный подход заключается в использовании функции posix_isatty(), например, в дескрипторе файла stdout, например:
if (posix_isatty(STDOUT))
/* do interactive terminal stuff here */
Я не знаю конкретно о PHP, но вы можете подняться по дереву процессов, пока не найдете либо инициализацию, либо cron.
Предполагая, что PHP может получить свой собственный идентификатор процесса и запускать внешние команды, необходимо выполнить ps -ef | grep pid
, где pid - ваш собственный идентификатор процесса, и извлечь из него идентификатор родительского процесса (PPID).
Затем сделайте то же самое с этим идентификатором PPID, пока вы не достигнете cron в качестве родителя или инициализации в качестве родителя.
Например, это мое дерево процессов, и вы можете смотрите цепочку владения, 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 или нет - вы можете забыть сделать это в своем интерактивном сеансе и все остальное.
Насколько я знаю, нет - вероятно, самое простое решение - предоставить дополнительный параметр самостоятельно, чтобы сообщить сценарию, как он был вызван.
Я бы заглянул в $_ENV
(var_dump()) и проверил, заметили ли вы разницу при запуске по сравнению с когда его запускает закадычный друг. Кроме того, я не думаю, что существует "официальный" переключатель, который расскажет вам, что произошло.
Жутко. Попробуйте
if (!isset($_SERVER['HTTP_USER_AGENT'])) {
Вместо этого. Двоичный файл PHP-клиента не отправляет его. Тип термина работает только тогда, когда PHP используется в качестве модуля (т. Е. apache), но при запуске php через интерфейс CGI используйте пример выше!
В моей среде я обнаружил, что TERM
был установлен в $_SERVER
при запуске из командной строки, но не установлен при запуске через Apache в качестве веб-запроса. Я поместил это в верхней части своего сценария, который я мог бы запустить из командной строки или получить доступ через веб-браузер:
if (isset($_SERVER{'TERM'}))
{
class::doStuffShell();
}
else
{
class::doStuffWeb();
}
$_SERVER['SESSIONNAME']
содержит Console
, если выполняется из командной строки. Может быть, это поможет.
В команде cron добавьте ?source=cron
в конец пути к сценарию. Затем в своем сценарии проверьте $_GET['source']
.
РЕДАКТИРОВАТЬ: извините, это сценарий оболочки, поэтому я не могу использовать qs. Я думаю, вы можете передать аргументы в форме php script.php arg1 arg2
, а затем прочитать их с помощью $argv
.
Другим вариантом было бы протестировать определенную переменную среды, которая устанавливается при вызове 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
Сравните различия и проверьте соответствующую переменную.
posix_isatty(STDOUT) return FALSE
если вывод вызова командной строки перенаправлен (канал или файл)...
if(!$_SERVER['HTTP_HOST']) {
blabla();
}
Я думаю, что было бы лучше запустить команду cron с дополнительной опцией в командной строке, которую вы не стали бы запускать вручную.
Cron сделал бы:
command ext_updates=1
Руководство будет делать:
command
Просто добавьте опцию в сам скрипт, чтобы параметр ext_updates имел значение по умолчанию false.
Это очень просто. Демоны Cron всегда экспортируют переменную среды MAILTO
. Проверьте, существует ли он и имеет непустое значение - тогда вы бежите из cron.
Для меня это легко... Просто count($_SERVER['argc'])
, и если у вас результат выше нуля, у вас закончится сервер. Вам просто нужно добавить в $_SERVER['argv']
свою пользовательскую переменную, например "CronJob"=true;