Почему мой crontab не работает, и как я могу устранить его?

226

Это канонический вопрос об использовании cron & crontab.

Вас направили сюда, потому что сообщество достаточно уверено, что ответ на ваш вопрос можно найти ниже. Если на ваш вопрос нет ответа ниже, ответы помогут вам собрать информацию, которая поможет сообществу помочь вам. Эта информация должна быть отредактирована в исходном вопросе.

Ответ: « Почему мой crontab не работает, и как я могу устранить его? 'можно увидеть ниже. Это относится к cronсистеме с выделенным crontab.

Эрик Лещинский
источник
2
Это огромный обман Причины, почему crontab не работает на AskUbuntu.
Дан Дакалеску
1
@DanDascalescu Похоже, Эрику нужно больше представителей
я самый глупый человек
1
Я только что присоединился к Server Fault SE (так что всего 101 повтор), но хотел бы дать этому вопросу -1 !! Этот вопрос был задуман только для получения репутации? @IamtheMostStupidPerson Полностью согласен с вами ...
Holyprogrammer
Западная идеология у этих 13-летних падаванов одновременно и учебная, и ослепительная, как сверхновая. Чтобы ответить на оба ваших вопроса: да, я сделал это для представителя, и да, Эрик должен получить больше репутации. Сколько еще повторений мне нужно ?? Больше. youtu.be/IaDt9T7BF38?t=262
Эрик Лещинский

Ответы:

317

Как исправить все ваши проблемы / проблемы, связанные с crontab (Linux)


Это вики сообщества , если вы заметили что-то неправильное в этом ответе или у вас есть дополнительная информация, пожалуйста, отредактируйте ее.


Во-первых, основная терминология:

  • cron (8) - это демон, который выполняет запланированные команды.
  • crontab (1) - программа, используемая для изменения пользовательских файлов crontab (5).
  • crontab (5) - это файл для каждого пользователя, который содержит инструкции для cron (8).

Далее образование о cron:

Каждый пользователь в системе может иметь свой собственный файл crontab. Расположение корневых и пользовательских файлов crontab зависит от системы, но обычно они указаны ниже /var/spool/cron.

Существует общесистемный /etc/crontabфайл, /etc/cron.dкаталог может содержать фрагменты crontab, которые также читаются и обрабатываются cron. Некоторые дистрибутивы Linux (например, Red Hat) также имеют /etc/cron.{hourly,daily,weekly,monthly}каталоги, скрипты внутри которых будут выполняться каждый час / день / неделю / месяц с привилегиями root.

root всегда может использовать команду crontab; обычные пользователи могут или не могут получить доступ. Когда вы редактируете файл crontab с помощью команды crontab -eи сохраняете его, crond проверяет его на базовую достоверность, но не гарантирует, что ваш файл crontab сформирован правильно. Существует файл с именем, в cron.denyкотором будет указано, какие пользователи не могут использовать cron. Расположение cron.denyфайла зависит от системы и может быть удалено, что позволит всем пользователям использовать cron.

Если компьютер не включен или демон crond не запущен, а дата / время выполнения команды истекли, crond не выполнит догон и не выполнит прошлые запросы.

Подробности crontab, как сформулировать команду:

Команда crontab представлена ​​одной строкой. Вы не можете использовать \для расширения команды на несколько строк. Знак hash ( #) представляет комментарий, который означает, что все в этой строке игнорируется cron. Ведущие пробелы и пустые строки игнорируются.

Будьте ОЧЕНЬ осторожны при использовании %знака процента ( ) в вашей команде. Если они не экранированы, \%они преобразуются в символы новой строки, и все, что после первого неэкранированного %, передается вашей команде на стандартный ввод.

Существует два формата файлов crontab:

  • Пользователь crontabs

    # Example of job definition:
    # .---------------- minute (0 - 59)
    # |  .------------- hour (0 - 23)
    # |  |  .---------- day of month (1 - 31)
    # |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
    # |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7)
    # |  |  |  |  |
    # *  *  *  *  *   command to be executed
    
  • Система в целом /etc/crontabи /etc/cron.dфрагменты

    # Example of job definition:
    # .---------------- minute (0 - 59)
    # |  .------------- hour (0 - 23)
    # |  |  .---------- day of month (1 - 31)
    # |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
    # |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7)
    # |  |  |  |  |
    # *  *  *  *  * user-name  command to be executed
    

Обратите внимание, что последний требует имени пользователя. Команда будет запущена от имени указанного пользователя.

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

  • Поля разделены пробелами или табуляцией.
  • Запятая ( ,) используется для указания списка, например, 1,4,6,8, что означает 1,4,6,8.
  • Диапазоны указываются с помощью тире ( -) и могут сочетаться со списками, например 1-3,9-12, что означает от 1 до 3, затем от 9 до 12.
  • Символ /может быть использован, чтобы ввести шаг, например, 2/5, что означает, начиная с 2, затем каждые 5 (2,7,12,17,22 ...). Они не закрывают конец.
  • Звездочка ( *) в поле обозначает весь диапазон для этого поля (например, 0-59для минутного поля).
  • Диапазоны и шаги могут быть объединены, например, */2означает, начиная с минимума для соответствующего поля, затем каждые 2, например, 0 для минут (0,2 ... 58), 1 для месяцев (1,3 ... 11) и т. Д.

Отладка команд cron

Проверьте почту!

По умолчанию cron отправляет любые выходные данные команды пользователю, для которого она выполняет команду. Если нет вывода, не будет почты. Если вы хотите, чтобы cron отправлял почту на другую учетную запись, вы можете установить переменную среды MAILTO в файле crontab, например

MAILTO=user@somehost.tld
1 2 * * * /path/to/your/command

Захватите результат самостоятельно

Вы можете перенаправить stdout и stderr в файл. Точный синтаксис для захвата вывода может варьироваться в зависимости от того, что использует оболочка cron. Вот два примера, которые сохраняют весь вывод в файл по адресу /tmp/mycommand.log:

1 2 * * * /path/to/your/command &>/tmp/mycommand.log
1 2 * * * /path/to/your/command >/tmp/mycommand.log 2>&1

Посмотри логи

Cron регистрирует свои действия через системный журнал, который (в зависимости от вашей настройки) часто идет в /var/log/cronили /var/log/syslog.

При необходимости вы можете отфильтровать операторы cron, например:

grep CRON /var/log/syslog 

Теперь, когда мы рассмотрели основы cron, где находятся файлы и как их использовать, давайте рассмотрим некоторые распространенные проблемы.

Проверьте, что cron работает

Если cron не запущен, ваши команды не будут запланированы ...

ps -ef | grep cron | grep -v grep

должен получить что-то вроде

root    1224   1  0 Nov16 ?    00:00:03 cron

или же

root    2018   1  0 Nov14 ?    00:00:06 crond

Если нет, перезапустите его

/sbin/service cron start

или же

/sbin/service crond start

Там могут быть другие методы; используйте то, что обеспечивает ваш дистрибутив.

cron запускает вашу команду в ограниченной среде.

Какие переменные среды доступны, вероятно, будет очень ограниченным. Как правило, вы получите только несколько переменных , определенных, например $LOGNAME, $HOMEи $PATH.

Особого внимания заслуживает PATHтолько /bin:/usr/bin. Подавляющее большинство проблем «мой cron скрипт не работает» вызван этим ограничительным путем . Если ваша команда находится в другом месте, вы можете решить эту проблему несколькими способами:

  1. Укажите полный путь к вашей команде.

    1 2 * * * /path/to/your/command
    
  2. Укажите подходящий путь в файле crontab

    PATH=/usr:/usr/bin:/path/to/something/else
    1 2 * * * command 
    

Если вашей команде требуются другие переменные окружения, вы также можете определить их в файле crontab.

cron запускает вашу команду с помощью cwd == $ HOME

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

Последняя команда в моем crontab не запускается

Cron обычно требует, чтобы команды заканчивались новой строкой. Отредактируйте свой crontab; перейдите в конец строки, содержащей последнюю команду, и вставьте новую строку (нажмите ввод).

Проверьте формат crontab

Вы не можете использовать пользовательский crontab в формате / crontab для / etc / crontab или фрагменты в /etc/cron.d и наоборот. Crontab, отформатированный пользователем, не содержит имя пользователя в 6-й позиции строки, в то время как crontab, отформатированный системой, включает имя пользователя и запускает команду от имени этого пользователя.

Я помещаю файл в /etc/cron. enjhourly,daily,weekly,monthly}, и он не запускается

  • Убедитесь, что имя файла не имеет расширения, см. Run-parts
  • Убедитесь, что файл имеет разрешения на выполнение.
  • Скажите системе, что использовать при выполнении вашего скрипта (например, поставить #!/bin/shсверху)

Cron даты, связанные с ошибками

Если ваша дата была недавно изменена в результате обновления пользователя или системы, часового пояса или другого, то crontab начнет работать неправильно и выдает странные ошибки, иногда работающие, иногда нет. Это попытка crontab попытаться «сделать то, что вы хотите», когда время изменится из-под него. Поле «минуты» станет недействительным после смены часа. В этом случае принимаются только звездочки. Перезапустите cron и попробуйте снова, не подключаясь к Интернету (чтобы у даты не было возможности перезагрузить один из серверов времени).

Знаки процента, опять

Чтобы подчеркнуть совет о знаках процента, вот пример того, что cron делает с ними:

# cron entry
* * * * * cat >$HOME/cron.out%foo%bar%baz

создаст файл ~ / cron.out, содержащий 3 строки

foo
bar
baz

Это особенно навязчиво при использовании dateкоманды. Обязательно избегайте знаков процента

* * * * * /path/to/command --day "$(date "+\%Y\%m\%d")"
Эрик Лещинский
источник
Возможно, вы захотите также упомянуть в разделе «ограниченное env», что LD_LIBRARY_PATH также может потребоваться установить дополнительные каталоги в случае сбоя вашей задачи cron из-за невозможности найти общие библиотеки.
DavidJ
обратите внимание, что вы даже можете написать что-то вроде этого: 35 1,5-23 / 2 * * * do_something вместо 35,1,5,7,9, .. * * * Кроме того, этот crontab.guru переводит записи, которые вы делаете в человеческий язык.
Деннис Нольте
1
У меня выходной захват не работает, может быть из-за оболочки sh. Я думаю, что это более переносимо: ... /path/to/your/command >/tmp/mycommand.log 2>&1
Chus
это сработало для меня:sudo apt-get install postfix
jmunsch
работа cron также зависит от того, насколько тяжелый файл? Поскольку я управлял простым привет миром на python с помощью cron, это сработало. Но мой второй код был немного тяжелым и обычно выполнялся, но с помощью cron он не дает никакого вывода в файл.
Девендра Бхат
22

Debian Linux и его производные (Ubuntu, Mint и т. Д.) Имеют некоторые особенности, которые могут помешать выполнению ваших заданий cron; в частности, файлы в /etc/cron.d, /etc/cron.{hourly,daily,weekly,monthly}должны:

  • быть владельцем root
  • быть доступным для записи только пользователю root
  • не может быть доступно для записи группе или другим пользователям
  • иметь имя без точек "." или любой другой специальный символ, кроме «-» и «_».

Последний ранит регулярно ничего не подозревающих пользователей; в частности , любой скрипт , в одной из этих папок с именем whatever.sh, mycron.py, testfile.plи т.д. , будут не выполняться, когда - либо.

По моему опыту, этот конкретный момент был наиболее частой причиной невыполнения cronjob в Debian и его производных.

Смотрите man cronдля более подробной информации, если это необходимо.

wazoox
источник
19

Если ваши cronjobs перестают работать, проверьте, что срок действия вашего пароля не истек., Поскольку после этого все задания cron прекращаются.
Будут сообщения, /var/log/messagesподобные приведенным ниже, которые показывают проблемы с аутентификацией пользователя:

(username) FAILED to authorize user with PAM (Authentication token is no longer valid; new one required)

Munkeh72
источник
2
Только что получил это (файл сообщения об ошибке / var / log / syslog для меня). В моем случае окно DigitalOcean, которое во время создания сбрасывает пароль root (необязательно) на другой, и, очевидно, пока вы не зайдете туда и не измените его, все задания cron не запускаются. Облом. Исправление это что-то вродеsudo -u root passwd
rogerdpack
12

Необычные и нерегулярные графики

Cron - это все, что считается очень простым планировщиком, а синтаксис не позволяет администратору легко формулировать немного более необычные графики.

Рассмотрим следующую работу, которая обычно объясняется как «запускать commandкаждые 5 минут» :

*/5 * * * * /path/to/your/command

против:

*/7 * * * * /path/to/your/command

который не всегда работает commandкаждые 7 минут .

Помните, что /символ может быть использован для введения шага, но шаги не переносятся за пределы конца серии, например, */7который соответствует каждой седьмой минуте минут, 0-59 т.е. 0,7,14,21,28,35,42,49, 56 , но между один час и следующий будет только 4 минуты между партиями , после того, как 00:56новая серия начинается 01:00, и 01:07т.д. (и партии не будет работать на 01:03, 01:10, и 01:17т.д.).


Что делать вместо этого?

Создать несколько партий

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

Например, чтобы запускать пакет каждые 40 минут (00:00, 00:40, 01:20, 02:00 и т. Д.), Создайте две партии: одну, которая запускается дважды в четные часы, и вторую, которая запускает только нечетные часы:

# The following lines create a batch that runs every 40 minutes i.e.

# runs on   0:00, 0:40,        02:00, 02:40         04:00 etc to 22:40
0,40 */2 * * * /path/to/your/command

# runs on               01:20,               03:20,       etc to 23:20
20 1/2 * * * /path/to/your/command

# Combined: 0:00, 0:40, 01:20, 02:00, 02:40, 03:20, 04:00 etc.

Запускайте свои партии реже

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

Запускайте свои партии чаще (но не допускайте одновременного запуска нескольких партий)

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

Вместо этого думайте по-другому и создайте cronjob, который изящно потерпит неудачу, когда предыдущий прогон еще не закончился, но который будет работать иначе. Смотрите этот Q & A :

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job 

Это почти сразу же начнет новый запуск после того, как завершится предыдущий запуск / usr / local / bin / частый_cron_job.

Начните свои партии чаще (но выходите изящно, когда условия не правильны)

Поскольку синтаксис cron ограничен, вы можете решить разместить более сложные условия и логику в самом пакетном задании (или в сценарии-оболочке вокруг существующего пакетного задания). Это позволяет вам использовать расширенные возможности ваших любимых языков сценариев, комментировать ваш код и предотвращать трудно читаемые конструкции в самой записи crontab.

В bash, то seven-minute-jobбудет выглядеть примерно так:

#!/bin/bash
# seven-minute-job
# This batch will only run when 420 seconds (7 min) have passed
# since the file /tmp/lastrun was either created or updated

if [ ! -f /tmp/lastrun ] ; then
    touch /tmp/lastrun
fi

if [ $(( $(date +%s) - $(date -r /tmp/lastrun +%s) )) -lt 420 ] ; then
     # The minimum interval of 7 minutes between successive batches hasn't passed yet.
    exit 0
fi

####  Start running your actual batch job below

/path/to/your/command

#### actual batch job is done, now update the time stamp
date > /tmp/lastrun
#EOF

Который вы можете затем безопасно (попытаться) запустить каждую минуту:

* * * * * /path/to/your/seven-minute-job

Другая, но похожая проблема заключалась бы в том, чтобы запланировать запуск пакета в первый понедельник каждого месяца (или во вторую среду) и т. Д. Просто запланируйте запуск пакета каждый понедельник и выходите, когда дата не находится между 1- м или 7- м и день недели не понедельник.

#!/bin/bash
# first-monday-of-the-month-housekeeping-job

# exit if today is not a Monday (and prevent locale issues by using the day number) 
if [ $(date +%u) != 1 ] ; then
  exit 0
fi

# exit if today is not the first Monday
if [ $(date +%d) -gt 7 ] ; then
  exit 0
fi

####  Start running your actual batch job below

/path/to/your/command

#EOF

Который вы можете безопасно (попытаться) запустить каждый понедельник:

0 0 * * 1 /path/to/your/first-monday-of-the-month-housekeeping-job

Не используйте cron

Если ваши потребности сложны, вы можете рассмотреть возможность использования более продвинутого продукта, который предназначен для запуска сложных расписаний (распределенных по нескольким серверам) и который поддерживает триггеры, зависимости заданий, обработку ошибок, повторные попытки и мониторинг повторных попыток и т. Д. " планирование работы и / или„автоматизация рабочей нагрузки“.

HBruijn
источник
8

PHP конкретные

Если у вас есть работа cron, например:

php /bla/bla/something.php >> /var/logs/somelog-for-stdout.log

И в случае ошибок ожидайте, что они будут отправлены вам, а они нет - проверьте это.

PHP по умолчанию не отправляет ошибки в STDOUT. @ смотри https://bugs.php.net/bug.php?id=22839

Чтобы исправить это, добавьте в файл php.ini или в свою строку (или в свою оболочку bash для PHP) эти:

  • --define display_startup_errors = 1
  • --define display_errors = 'stderr'

Первая настройка позволит вам иметь летальные исходы, такие как «Memory oops», а вторая - перенаправить их всех в STDERR. Только после того, как вы сможете хорошо выспаться, все будет отправлено на почту вашего root, а не только в журнал.

gaRex
источник
2
Этот отчет об ошибке был закрыт еще в 2007 году, и состояние ветки было добавлено в ветки PHP 5.2+. Вы уверены, что это нужно? Я только что попробовал на PHP 5.4, и, кажется, работает нормально. (Это все еще необходимо для PHP 4, хотя).
Xeoncross
@Xeoncross см. Дату ответа :)
gaRex
1
Да, это то, что смутило меня, так как вы ответили в 2013 году, а билет был в '07.
Xeoncross
0

Добавив мой ответ отсюда для полноты, и добавив еще один потенциально полезный ресурс:

cronПользователь имеет другой , $PATHчем вы:

Частая проблема пользователи делают с crontabзаписями в том , что они забывают , что cronработает в другом , environmentчем они делают, вошедшим в системе пользователя. Например, пользователь создает программу или сценарий в своем $HOMEкаталоге и вводит следующую команду для его запуска:

$ ./certbot ... 

Команда отлично запускается из его командной строки. Затем пользователь добавляет эту команду к своей crontab, но находит, что это не работает:

*/10 * * * * ./certbot ....

Причиной сбоя в этом случае является то, что ./местоположение cronпользователя отличается от местоположения вошедшего в систему пользователя. То environmentесть отличается! PATH является частью environment, и он обычно отличается для cronпользователя. Осложняет эту проблему то, что environmentfor cronне одинаков для всех дистрибутивов * nix , и существует несколько версийcron

Простое решение этой конкретной проблемы - дать cronпользователю полную спецификацию пути в crontabзаписи:

0 22 * * * /path/to/certbot .....

Что такое cronпользователь environment?

В некоторых случаях нам может понадобиться полная environmentспецификация для cronнашей системы (или нам может быть просто любопытно). Что такое environmentдля cronпользователя и чем оно отличается от нашего? Кроме того, нам может потребоваться информация environmentдля другого cronпользователя - rootнапример ... что rootпользователь environmentиспользует cron? Один из способов узнать это - попросить cronнас сказать:

  1. Создайте сценарий оболочки в вашей домашней директории ( ~/) следующим образом (или с вашим редактором):
$ nano ~/envtst.sh
  1. Введите следующее в редакторе после настройки для вашей системы / пользователя:
#!/bin/sh 
/bin/echo "env report follows for user "$USER >> /home/you/envtst.sh.out 
/usr/bin/env >> /home/you/envtst.sh.out 
/bin/echo "env report for user "$USER" concluded" >> /home/you/envtst.sh.out
/bin/echo " " >> /home/you/envtst.sh.out
  1. Сохраните файл, выйдите из редактора и установите права доступа к файлу как исполняемые.
$ chmod a+rx ~/envtst.sh
  1. Запустите только что созданный сценарий и просмотрите вывод /home/you/envtst.sh.out. Эти выходные данные будут отображать текущую среду, в $USERкоторую вы вошли как:
$ ./envtst.sh $$ cat /home/you/envtst.sh.out
  1. Откройте ваш crontabдля редактирования:
$ crontab -e -u root
  1. Введите следующую строку в нижней части вашего crontab:
* * * * *  /home/you/envtst.sh >> /home/you/envtst.sh.err 2>&1

ОТВЕТ: Выходной файл /home/you/envtst.sh.outбудет содержать список environmentдля «пользователя root cron». Как только вы это узнаете, измените вашу crontabзапись соответственно.

Я не могу указать график, который мне нужен в моей crontabзаписи:

Запись в расписании для, crontabконечно, определена в man crontab, и вы должны прочитать это. Тем не менее, чтение man crontabи понимание графика две разные вещи. И метод проб и ошибок в спецификации расписания может стать очень утомительным. К счастью, есть ресурс, который может помочь: гуру crontab. , Введите спецификацию вашего расписания, и оно объяснит расписание на простом английском языке.

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

Симус
источник