Почему не работают скрипты crontab?
Часто, crontab
сценарии не выполняются по расписанию или как ожидается. Для этого есть множество причин:
- неправильная запись crontab
- проблема с разрешениями
- переменные среды
Вики-сообщество призвано объединить основные причины crontab
сценарии не выполняются, как ожидалось. Запишите каждую причину в отдельном ответе.
Пожалуйста, включите одну причину для ответа - подробности о том, почему он не выполнен - и исправьте (и) по этой одной причине.
Пожалуйста, пишите только специфичные для cron проблемы, например команды, которые выполняются как ожидается от оболочки, но ошибочно выполняются cron.
47 ответов
Другая среда
Cron передает минимальный набор переменных среды для ваших заданий. Чтобы увидеть разницу, добавьте фиктивную работу вот так:
* * * * * env> /tmp/env.output
Ждать /tmp/env.output
чтобы быть созданным, затем удалите работу снова. Теперь сравните содержимое /tmp/env.output
с выходом env
запустить в своем обычном терминале.
Распространенная "гоча" здесь PATH
Переменная окружения отличается. Может быть, ваш скрипт cron использует команду somecommand
нашел в /opt/someApp/bin
, который вы добавили в PATH
в /etc/environment
? Крон игнорирует PATH
из этого файла, так что бег somecommand
из вашего скрипта не получится при запуске с cron, но сработает при запуске в терминале. Стоит отметить, что переменные из /etc/environment
будут переданы заданиям cron, но не переменным, которые специально устанавливает сам cron, таким как PATH
,
Чтобы обойти это, просто установите свой собственный PATH
переменная в верхней части скрипта. Например
#!/bin/bash
PATH=/opt/someApp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# rest of script follows
Некоторые предпочитают просто использовать абсолютные пути ко всем командам. Я рекомендую против этого. Подумайте, что произойдет, если вы хотите запустить свой скрипт в другой системе, и в этой системе команда находится в /opt/someAppv2.2/bin
вместо. Вам придется пройти весь сценарий, заменив /opt/someApp/bin
с /opt/someAppv2.2/bin
вместо того, чтобы делать небольшое редактирование в первой строке скрипта.
Вы также можете установить переменную PATH в файле crontab, который будет применяться ко всем заданиям cron. Например
PATH=/opt/someApp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
15 1 * * * backupscript --incremental /home /root
Мой главный вопрос: если вы забудете добавить новую строку в конце crontab
файл. Другими словами, файл crontab должен заканчиваться пустой строкой.
Ниже приведен соответствующий раздел в справочных страницах по этому вопросу (man crontab
потом переходи до конца)
Although cron requires that each entry in a crontab end in a newline
character, neither the crontab command nor the cron daemon will detect
this error. Instead, the crontab will appear to load normally. However,
the command will never run. The best choice is to ensure that your
crontab has a blank line at the end.
4th Berkeley Distribution 29 December 1993 CRONTAB(1)
Cron демон не работает. Я действительно облажался с этим несколько месяцев назад.
Тип:
pgrep cron
Если вы не видите номера, значит, cron не запущен. sudo /etc/init.d/cron start
можно использовать для запуска cron.
РЕДАКТИРОВАТЬ: Вместо того, чтобы вызывать сценарии инициализации через /etc/init.d, используйте служебную утилиту, например
sudo service cron start
Имя файла скрипта в cron.d/
, cron.daily/
, cron.hourly/
и т. д., не должны содержать точку (.
), иначе run-parts пропустит их.
Смотрите run-parts(8):
If neither the --lsbsysinit option nor the --regex option is given then
the names must consist entirely of upper and lower case letters, dig‐
its, underscores, and hyphens.
If the --lsbsysinit option is given, then the names must not end in
.dpkg-old or .dpkg-dist or .dpkg-new or .dpkg-tmp, and must belong to
one or more of the following namespaces: the LANANA-assigned namespace
(^[a-z0-9]+$); the LSB hierarchical and reserved namespaces
(^_?([a-z0-9_.]+-)+[a-z0-9]+$); and the Debian cron script namespace
(^[a-zA-Z0-9_-]+$).
Итак, если у вас есть скрипт cron backup.sh
, analyze-logs.pl
в cron.daily/
каталог, вам лучше удалить имена расширений.
Во многих средах cron выполняет команды, используя sh
в то время как многие люди предполагают, что он будет использовать bash
,
Предложения для проверки или исправления ошибки команды:
Попробуйте запустить команду в
sh
чтобы увидеть, работает ли это:sh -c "mycommand"
Оберните команду в подоболочку bash, чтобы убедиться, что она запускается в bash:
bash -c "mybashcommand"
Скажите cron запустить все команды в bash, установив оболочку в верхней части вашего crontab:
SHELL=/bin/bash
Если команда является скриптом, убедитесь, что скрипт содержит шебанг:
#!/bin/bash
У меня были некоторые проблемы с часовыми поясами. Cron работал со свежим часовым поясом установки. Решение было перезапустить cron:
sudo service cron restart
Абсолютный путь должен использоваться для скриптов:
Например, /bin/grep
следует использовать вместо grep
:
# m h dom mon dow command
0 0 * * * /bin/grep ERROR /home/adam/run.log &> /tmp/errors
Вместо:
# m h dom mon dow command
0 0 * * * grep ERROR /home/adam/run.log &> /tmp/errors
Это особенно сложно, потому что та же команда будет работать при запуске из оболочки. Причина в том, что cron
не имеет то же самое PATH
переменная окружения как пользователь.
Если ваша команда crontab имеет %
символ в этом, cron пытается интерпретировать это. Так что, если вы использовали какую-либо команду с %
в нем (например, указание формата для команды date) вам необходимо его избежать.
Это и другие хорошие ошибки здесь:
http://www.pantz.org/software/cron/croninfo.html
Cron вызывает скрипт, который не является исполняемым.
Запустив chmod +x /path/to/scrip
скрипт становится исполняемым и должен решить эту проблему.
Также возможно, что срок действия пароля пользователя истек. Даже пароль root может истечь. Вы можете tail -f /var/log/cron.log
и вы увидите сбой cron с истекшим сроком действия пароля. Вы можете установить пароль, чтобы никогда не истек, делая это: passwd -x -1 <username>
В некоторых системах (Debian, Ubuntu) ведение журнала для cron не включено по умолчанию. В /etc/rsyslog.conf или /etc/rsyslog.d/50-default.conf строка:
# cron.* /var/log/cron.log
следует отредактировать (sudo nano /etc/rsyslog.conf
) оставлено без комментариев:
cron.* /var/log/cron.log
После этого вам нужно перезапустить rsyslog через
/etc/init.d/rsyslog restart
или же
service rsyslog restart
Источник: Включить ведение журнала crontab в Debian Linux
В некоторых системах (Ubuntu) отдельный файл регистрации для cron не включен по умолчанию, но связанные с cron журналы появляются в файле syslog. Можно использовать
cat /var/log/syslog | grep cron -i
для просмотра сообщений, связанных с cron.
Если ваш cronjob вызывает GUI-приложения, вы должны сообщить им, какой DISPLAY они должны использовать.
Пример: запуск Firefox с помощью cron.
Ваш скрипт должен содержать export DISPLAY=:0
где-то.
Небезопасное разрешение таблицы cron
Таблица cron отклоняется, если ее разрешение небезопасно
sudo service cron restart
grep -i cron /var/log/syslog|tail -2
2013-02-05T03:47:49.283841+01:00 ubuntu cron[49906]: (user) INSECURE MODE (mode 0600 expected) (crontabs/user)
Проблема решена с
# correct permission
sudo chmod 600 /var/spool/cron/crontabs/user
# signal crond to reload the file
sudo touch /var/spool/cron/crontabs
Боюсь, проблемы с разрешениями встречаются довольно часто.
Обратите внимание, что распространенным обходным решением является выполнение всего, используя crontab root, который иногда является действительно плохой идеей. Установка правильных разрешений - определенно упущенная проблема.
Сценарий зависит от местоположения. Это связано с тем, что в скрипте всегда используются абсолютные пути, но это не совсем одно и то же. Ваша работа cron может потребоваться cd
перед запуском в конкретный каталог, например, задача rake в приложении Rails может потребоваться в корне приложения для Rake, чтобы найти правильную задачу, не говоря уже о соответствующей конфигурации базы данных и т. д.
Итак, запись в crontab
23 3 * * * /usr/bin/rake db:session_purge RAILS_ENV=production
будет лучше как
23 3 * * * cd /var/www/production/current && /usr/bin/rake db:session_purge RAILS_ENV=production
Или, чтобы сделать запись в crontab более простой и менее хрупкой:
23 3 * * * /home/<user>/scripts/session-purge.sh
со следующим кодом в /home/<user>/scripts/session-purge.sh
:
cd / var / www / production / current / usr / bin / rake db: session_purge RAILS_ENV = production
Спецификации Crontab, которые работали в прошлом, могут сломаться при перемещении из одного файла crontab в другой. Иногда причина в том, что вы переместили спецификацию из системного файла crontab в пользовательский файл crontab или наоборот.
Формат спецификации задания cron отличается в файлах crontab пользователей (/var/spool/cron/username или /var/spool/cron/crontabs/username) и системных crontabs (/etc/crontab
и файлы в /etc/cron.d
).
Системные crontabs имеют дополнительное поле 'user' прямо перед командой для запуска.
Это приведет к ошибкам с указанием таких вещей, как george; command not found
когда вы перемещаете команду из /etc/crontab
или файл в /etc/cron.d
в файл crontab пользователя.
И наоборот, cron будет выдавать такие ошибки, как /usr/bin/restartxyz is not a valid username
или подобное, когда происходит обратное.
Самая частая причина, по которой я видел сбой cron в неверно указанном расписании. Требуется практика, чтобы указать работу, запланированную на 23:15 как 30 23 * * *
вместо * * 11 15 *
или же 11 15 * * *
, День недели для рабочих мест после полуночи также сбивается с толку 2-6
после полуночи нет 1-5
, Конкретные даты обычно являются проблемой, так как мы редко их используем * * 3 1 *
не 3 марта.
Если вы работаете с различными платформами, используя неподдерживаемые параметры, такие как 2/3
во времени спецификации также могут вызвать сбои. Это очень полезная опция, но она не всегда доступна. Я также сталкивался с проблемами, такие как списки 1-5
или же 1,3,5
,
Использование неквалифицированных путей также вызвало проблемы. Путь по умолчанию обычно /bin:/usr/bin
поэтому будут выполняться только стандартные команды. Эти каталоги обычно не имеют желаемой команды. Это также влияет на сценарии, использующие нестандартные команды. Другие переменные среды также могут отсутствовать.
Полное уничтожение существующего crontab вызвало у меня проблемы. Я сейчас загружаю из файла копию. Это можно восстановить из существующего crontab, используя crontab -l
если это будет забито Я храню копию crontab в ~/bin. Это комментируется повсюду и заканчивается строкой # EOF
, Это загружается ежедневно из записи в crontab, например:
#! / USR / бен / кронтаб # Перезагрузить этот crontab # 54 12 * * * ${HOME}/bin/crontab
Приведенная выше команда reload опирается на исполняемый файл crontab с путём взрыва, на котором выполняется crontab. В некоторых системах требуется запуск crontab в команде и указание файла. Если каталог является сетевым, то я часто использую crontab.$(hostname)
как имя файла. Это в конечном итоге исправит случаи, когда неправильный crontab загружается на неправильный сервер.
Использование файла обеспечивает резервное копирование того, каким должен быть crontab, и позволяет выполнять временные изменения (единственный раз, когда я использую crontab -e
) для автоматического возврата. Доступны заголовки, которые помогают правильно настроить параметры планирования. Я добавил их, когда неопытные пользователи будут редактировать crontab.
Редко я сталкивался с командами, которые требуют ввода пользователя. Они терпят неудачу при crontab, хотя некоторые будут работать с перенаправлением ввода.
Если у вас есть такая команда:
* * * * * /path/to/script >> /tmp/output
и это не работает, и вы не можете видеть какой-либо вывод, это не обязательно означает, что cron не работает. Сценарий может быть поврежден, и вывод будет передан в stderr, который не будет передан в / tmp / output. Проверьте, что это не так, записав этот вывод:
* * * * * /path/to/script >> /tmp/output 2>&1
чтобы увидеть, поможет ли это вам решить вашу проблему.
Сценарий cron вызывает команду с параметром --verbose
У меня произошел сбой скрипта cron, потому что я набирал скрипт на автопилоте и включил опцию --verbose:
#!/bin/bash
some commands
tar cvfz /my/archive/file.tar.gz /my/shared/directory
come more commands
Сценарий выполнялся нормально при выполнении из оболочки, но не работал при запуске из crontab, поскольку подробный вывод выводится на стандартный вывод при запуске из оболочки, но нигде при запуске из crontab. Легко исправить, чтобы удалить "V":
#!/bin/bash
some commands
tar cfz /my/archive/file.tar.gz /my/shared/directory
some more commands
Если вы получаете доступ к учетной записи с помощью ключей SSH, можно войти в учетную запись, но не заметить, что пароль учетной записи заблокирован (например, из-за истечения срока действия или попытки ввода неверного пароля)
Если система использует PAM и учетная запись заблокирована, это может остановить запуск cronjob. (Я проверял это на Solaris, но не на Ubuntu)
Подобные сообщения вы можете найти в /var/adm/messages:
24 октября 07:51:00 mybox cron[29024]: [ID 731128 auth.notice] pam_unix_account: cron пытается проверить заблокированный аккаунт myuser с локального хоста 24 октября, 07:52:00 mybox cron[29063]: [ID 731128 auth.notice] pam_unix_account: cron пытается проверить заблокированный аккаунт myuser с локального хоста 24 октября 07:53:00 mybox cron[29098]: [ID 731128 auth.notice] pam_unix_account: cron пытается проверить заблокированный аккаунт myuser с локального хоста 24 октября, 07:54:00 mybox cron[29527]: [ID 731128 auth.notice] pam_unix_account: cron пытается проверить заблокированный аккаунт myuser с локального хоста
Все, что вам нужно сделать, это запустить:
# passwd -u <ИМЯ ПОЛЬЗОВАТЕЛЯ>
как root, чтобы разблокировать учетную запись, и crontab должен снова работать.
Я писал сценарий установки оболочки, который создает другой сценарий для очистки старых данных транзакций из базы данных. В рамках задачи его приходилось настраивать ежедневно cron
задание запускается в произвольное время, когда загрузка базы данных была низкой.
Я создал файл mycronjob
с расписанием cron, именем пользователя и командой и скопировал его в /etc/cron.d
каталог. Мои две ошибки:
mycronjob
файл должен был принадлежать пользователю root для запуска- Я должен был установить права доступа к файлу 644 - 664 не будет работать.
Проблема с разрешением появится в /var/log/syslog
как то похожее на:
Apr 24 18:30:01 ip-11-22-33-44 cron[40980]: (*system*) INSECURE MODE (group/other writable) (/etc/crontab)
Apr 24 18:30:01 ip-11-22-33-44 cron[40980]: (*system*) INSECURE MODE (group/other writable) (/etc/cron.d/user)
Первая строка относится к /etc/crontab
файл, а затем в файл, который я поместил под /etc/cront.d
,
=== Предупреждение Docker ===
Если вы используете докер,
Я считаю правильным добавить, что мне не удалось заставить cron работать в фоновом режиме.
Для запуска задания cron внутри контейнера я использовал supervisor и запустил cron -f
вместе с другим процессом.
Изменить: Еще одна проблема - мне также не удалось заставить его работать при запуске контейнера с сетью HOST. Смотрите эту проблему здесь также: https://github.com/phusion/baseimage-docker/issues/144
Если вы отредактировали свой файл crontab с помощью редактора Windows (через samba или что-то еще), и он заменил символы новой строки на \ n \ r или просто \ r, cron не запустится.
Кроме того, если вы используете /etc/cron.d/* и в одном из этих файлов есть \ r, cron будет перемещаться по файлам и останавливаться, когда попадет в плохой файл. Не уверен, что это проблема?
Использование:
od -c /etc/cron.d/* | grep \r
Cron-демон может быть запущен, но на самом деле не работает. Попробуйте перезапустить cron:
sudo /etc/init.d/cron restart
Когда задача запускается в cron, stdin закрывается. Программы, которые работают по-разному в зависимости от того, доступен ли stdin или нет, будут вести себя по-разному между сеансом оболочки и в cron.
Примером является программа goaccess
для анализа файлов журнала веб-сервера. Это не работает в cron:
goaccess -a -f /var/log/nginx/access.log > output.html
а также goaccess
показывает страницу справки вместо создания отчета. В оболочке это можно воспроизвести с помощью
goaccess -a -f /var/log/nginx/access.log > output.html < /dev/null
Исправление для goaccess
заставить его читать журнал из stdin вместо чтения из файла, поэтому решение состоит в том, чтобы изменить запись crontab на
cat /var/log/nginx/access.log | goaccess -a > output.html
Основываясь на том, что Аарон Пирт упомянул о подробном режиме, иногда сценарии, не находящиеся в подробном режиме, будут инициализироваться, но не завершаться, если поведение включенной команды по умолчанию будет выводить строку или более на экран после запуска процедуры. Например, я написал сценарий резервного копирования для нашей интрасети, который использовал curl, утилиту, которая загружает или загружает файлы на удаленные серверы, и это очень удобно, если вы можете получить доступ к указанным удаленным файлам только через HTTP. Использование 'curl http://something.com/somefile.xls' приводило к зависанию написанного мной скрипта, который никогда не завершался, поскольку он выплевывал новую строку, за которой следовала строка прогресса. Мне пришлось использовать флаг без вывода сообщений (-s), чтобы запретить выводить какую-либо информацию, и написать собственный код для обработки, если файл не удалось загрузить.
Запись в cron через "crontab -e" с аргументом username в строке. Я видел примеры пользователей (или системных администраторов), пишущих свои сценарии оболочки и не понимающих, почему они не автоматизируют. Аргумент "пользователь" существует в /etc/crontab, но не в пользовательских файлах. так, например, ваш личный файл будет выглядеть примерно так:
# m h dom mon dow command
* * */2 * * /some/shell/script
тогда как / etc / crontab будет:
# m h dom mon dow user command
* * */2 * * jdoe /some/shell/script
Итак, почему вы делаете последнее? Ну, в зависимости от того, как вы хотите установить свои разрешения, это может стать очень запутанным. Я написал сценарии для автоматизации задач для пользователей, которые не разбираются в тонкостях или не хотят беспокоиться о тяжелой работе. Установив разрешения для --x------
Я могу сделать сценарий исполняемым без возможности его чтения (и, возможно, случайного изменения). Тем не менее, я мог бы захотеть запустить эту команду с несколькими другими из одного файла (таким образом, облегчая поддержку), но удостовериться, что выходной файл назначен правильному владельцу. Это (по крайней мере, в Ubuntu 10.10) устраняет как невозможность чтения файла, так и выполнения, а также вышеупомянутую проблему с помещением точек в /etc/crontab (что, как ни странно, не вызывает ошибок при прохождении crontab -e
).
В качестве примера я видел случаи sudo crontab -e
используется для запуска скрипта с правами суперпользователя, с соответствующим chown username file_output
в сценарии оболочки. Небрежно, но это работает. ИМХО, более изящный вариант это положить в /etc/crontab
с объявленным именем пользователя и соответствующими разрешениями, так file_output
едет в нужное место и хозяину.
Файлы.bashrc по умолчанию в UBuntu 16.04 (и во многих других версиях) имеют встроенный механизм, который ничего не делает, если они не являются интерактивной оболочкой.
Это может помешать правильному источнику файла в скрипте, который запускается cron! Чтобы предотвратить это, закомментируйте следующие строки в верхней части вашего файла.bashrc:
Ubuntu 16.04
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
Ubuntu 12.04 и некоторые другие версии
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
До тех пор, пока они остаются, если cron запускает скрипт, который получает ваш файл.bashrc, вы фактически не получите никаких переменных окружения, к которым вы привыкли, внутри вашего терминала!
В 16.04, если у вас есть эта ошибка в системном журнале
(CRON) error (can't fork)
пытаться:
systemctl status cron.service
В результате:
Tasks: num_task (limit: 512)
Если num_task
близко к пределу использования:
systemctl set-property cron.service TasksMax=new_max
замещать new_max
с подходящим значением.
Хотя вы можете определять переменные окружения в вашем crontable, вы не находитесь в сценарии оболочки. Поэтому конструкции, подобные следующим, не будут работать:
SOME_DIR=/var/log
MY_LOG_FILE=${SOME_LOG}/some_file.log
BIN_DIR=/usr/local/bin
MY_EXE=${BIN_DIR}/some_executable_file
0 10 * * * ${MY_EXE} some_param >> ${MY_LOG_FILE}
Это потому, что переменные не интерпретируются в crontable: все значения принимаются буквально. И это то же самое, если вы опустите скобки. Таким образом, ваши команды не будут выполняться, а ваши файлы журнала не будут записаны...
Вместо этого вы должны определить все переменные окружения прямо:
SOME_DIR=/var/log
MY_LOG_FILE=/var/log/some_file.log
BIN_DIR=/usr/local/bin
MY_EXE=/usr/local/bin/some_executable_file
0 10 * * * ${MY_EXE} some_param >> ${MY_LOG_FILE}
Задания Cron не будут выполняться, если срок действия пароля вашего пользователя истек.
Одним из признаков этого является, если ваш журнал хрон (как правило, /var/log/syslog
в Ubuntu, я считаю) содержит сообщения типа "Токен аутентификации больше не действителен; требуется новый".
Вы можете проверить эту проблему, запустив sudo chage -l the_username
,
Вот пример просроченного пароля:
$ sudo chage -l root
Last password change : password must be changed
Password expires : password must be changed
Password inactive : password must be changed
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 14600
Number of days of warning before password expires : 14
Исправление заключается в изменении пароля. Например:
sudo -u root passwd
Затем следуйте инструкциям, указав новый пароль в соответствии с запросом.
Спасибо https://serverfault.com/questions/449651/why-is-my-crontab-not-working-and-how-can-i-troubleshoot-it за указание меня в правильном направлении здесь. В моем случае, так же, как и для этого комментатора, это был пользователь root для коробки DigitalOcean.