Привет, Хабр! Все, кто администрирует Linux, рано или поздно сталкивается с cron
- стандартным планировщиком задач. Но если настроить его "на скорую руку", можно обнаружить неприятные сюрпризы:
Скрипт не запустился, тк
cron
работает в другом окруженииСервер лёг от нагрузки, потому что 100 задач стартовали одновременно
Вы не узнали об ошибке из-за того, что вывод скрипта попал в /dev/null
В этой статье разбор не только основы работы с cron
, но и:
Продвинутые форматы расписания - как задавать сложные интервалы и комбинировать условия
Типичные подводные камни - работа с переменными окружения, логирование, управление параллельным выполнением
Альтернативы для сложных сценариев - когда
cron
уже недостаточно и стоит обратить внимание наsystemd.timer
А также, дополнительная информация:
Как избежать "падений" из-за наложения задач
Когда
cron
- хороший выбор, а когда лучше использовать другие инструменты
Статья будет полезна как начинающим администраторам, так и тем, кто хочет глубже разобраться в автоматизации задач в Linux.
Что такое cron
Cron - классический планировщик задач в Unix-подобных операционных системах, позволяющий автоматизировать выполнение команд и скриптов по расписанию. Позволяет запускать команды/скрипты в определенное время или с определенной периодичностью.
Состоит cron
из нескольких ключевых компонентов:
Демон
crond
- фоновый процесс, который запускается при старте системы и работает постоянноТаблицы
cron
(crontab
) - файлы с расписанием задачЖурнал выполнения - обычно записывается в
syslog
или отдельные логи
Демон cron
просыпается каждую минуту, проверяет crontab
и выполняет задачи, которые должны запуститься в текущую минуту.
Синтаксис:

Что это значит? Разберём несколько примеров:
0 * * * *
- каждый час в начале часа0 0 * * *
- каждый день в полночь0 0 * * 0
- каждое воскресенье в полночь*/15 * * * *
- каждые 15 минут0 4 1 * *
- первое число каждого месяца в 4:00 утра
Также, в cron
существуют "специальные строки" для часто используемых интервалов, частота которых предопределена:
@yearly
или@annually
- один раз в год (0 0 1 1 *)@monthly
- один раз в месяц (0 0 1 * *)@weekly
- один раз в неделю (0 0 * * 0)@daily
или@midnight
- один раз в день (0 0 * * *)@hourly
- один раз в час (0 * * * *)@reboot
- при запуске системы
Следующим важным аспектом являются переменные окружения. Cron выполняет команды в минимальном окружении:
PATH
- обычно очень ограничен (/usr/bin:/bin)SHELL
- обычно /bin/sh. Рекомендуется для сложных сценариев (поддержка массивов, функций)HOME
- домашний каталог пользователя. Подходит для задач, работающих с пользовательскими файламиMAILTO
- email для отправки результатов (можно использовать несколько, через запятую). Для отправки вsyslog
использовать:MAILTO=syslog
Крайне рекомендуется всегда указывать полные пути к командам и файлам в заданиях.
Почему рекомендуется указывать полные пути
Всегда указывать полные пути к командам и файлам в cron
-заданиях связана с особенностями работы и потенциальными проблемами, которые могут (но необязательно возникнут) при их игнорировании. Постараюсь учесть все возможные:
1. Ограниченное окружение cron
Когда cron запускает задание, оно выполняется в минимальном окружении, которое сильно отличается от вашего обычного пользовательского окружения:
Очень ограниченная переменная
PATH
(обычно только/usr/bin:/bin
)Отсутствуют многие пользовательские переменные окружения
Нет
.bashrc
,.profile
и других конфигурационных файлов
Пример проблемы:
* * * * * myscript.sh # Может не найти команду
В то время, как:
* * * * * /home/user/scripts/myscript.sh # Точно найдёт скрипт
2. Разные версии программ
Полные пути помогают избежать ситуаций, когда cron находит другую версию программы, которая нам не нужна.
Пример:
* * * * * python3 script.py # Какой именно python3?
Правильнее:
* * * * * /usr/local/bin/python3 /path/to/script.py
3. Проблемы с относительными путями
Cron выполняет команды с разными рабочими каталогами (часто это домашний каталог пользователя или корневой каталог). Относительные же пути могут вести не туда, куда изначально было запланировано.
Пример:
* * * * * ./script.sh # Где ищется этот файл?
Лучше:
* * * * * /absolute/path/to/script.sh
4. Проблемы с перенаправлением вывода
Перенаправление вывода может повести себя крайне неожиданно без полных путей.
Пример:
* * * * * some_command > output.log # Куда именно запишется файл?
Более верно:
* * * * * /usr/bin/some_command > /full/path/to/output.log
Как найти полные пути?
Помечаю для тех, кому мало знаком Linux. В данном вопросе поможет команда which
или whereis
:
which python3
# /usr/local/bin/python3
whereis bash
# bash: /bin/bash /usr/share/man/man1/bash.1.gz
Управление crontab-файлами
Основные команды для работы cron
:
Основные команды для работы с cron:
crontab -e
- редактирование crontab текущего пользователяcrontab -l
- просмотр текущего crontabcrontab -r
- удаление crontabcrontab -u username -e
- редактирование crontab другого пользователя (требует прав root)
Системные crontab-файлы обычно расположены в:
/etc/crontab
- системный crontab/etc/cron.d/
- каталог для дополнительных crontab-файлов/var/spool/cron/crontabs/
- каталог с пользовательскими crontab (в некоторых дистрибутивах)
Продвинутые возможности cron
В данном блоке разберём продвинутые возможности, которые могут быть знакомы не каждому. Они позволяют использовать не только простые временные интервалы, но и сложные шаблоны для точного управления планированием задач. Подобная гибкость позволяет адаптировать базовый планировщик под различные нужды - от периодического обслуживания до сложных многоэтапных процессов.
1. Сложные интервалы и комбинации
Cron
поддерживает гибкие форматы расписания, но не все знают, как их комбинировать. Далее приведу примеры использования различных "не базовых" интервалов и комбинаций:
Диапазоны (-
)
0 9-18 * * *
- каждый час с 9:00 до 18:000 0 1-7 * *
- первые 7 дней месяца в 00:00
Списки (,
)
0 0 1,15 * *
- 1-го и 15-го числа каждого месяца0 0 * * 1,3,5
- по понедельникам, средам и пятницам
Шаги (/
)
*/5 * * * *
- каждые 5 минут0 */3 * * *
- каждые 3 часа
Комбинирование
0 8-18/2 * * 1-5
- каждые 2 часа с 8:00 до 18:00, но только в рабочие дни0 0,12 1-10,15 * *
- в 00:00 и 12:00 с 1-го по 10-е и 15-го числа
2. Управление параллельным выполнением
Если задача выполняется дольше, чем интервал запуска - существует риск возникновения конфликтов. Возникает это по различным причинам:
Накопление процессов (например, выполнение задачи 10 минут, а интервал запуска - 5 минут, то через некоторое время будут работать несколько экземпляров скрипта)
Блокировка файлов и ресурсов (например, если скрипт работает с БД или логами, несколько экземпляров могут пытаться изменять идентичные данные одновременно)
Проблемы с состоянием (например, первый запуск меняет конфиг, а второй запуск об изменениях "не знает", и действует некорректно)
Ошибки в логировании (несколько процессов пишут в один лог. В лучшем случае данные просто перемешаются, в худшем - у Вас появится битый файл)
В данном случае мы можем решить проблему следующими способами:
Ограничение времени выполнения (
timeout
)
* * * * * timeout 300 /path/to/script.sh # Остановить через 5 минут
Блокировка через
flock
* * * * * /usr/bin/flock -n /tmp/myscript.lock /path/to/script.sh
Где -n
- не ждать, если скрипт уже работает (-w 10
- ждать 10 секунд, если требуется)
Что такое flock
flock
— утилита управления блокировкой файлов. Позволяет предотвратить одновременный запуск нескольких экземпляров скрипта, что критично для задач cron
, если:
Скрипт выполняется дольше, чем интервал между запусками
Несколько процессов могут конфликтовать при работе с общими ресурсами (файлами, БД и тд)
Синтаксис:
-n
- не ждать-w N
- ждать N секунд-x
- эксклюзивная блокировка (запрещает доступ всем процессам)-s
- разделяемая блокировка (разрешает остальным доступ на чтение)
3. Логирование и обработка ошибок
Перенаправление вывода:
>> file.log 2>&1
- stdout + stderr в один файл>> file.log 2>> error.log
- раздельные логи
Логирование с ротацией:
0 0 * * * /path/to/script.sh >> "/var/log/script_$(date +\%Y\%m\%d).log" 2>&1
Примеры сложных сценариев cron
Теория - безусловно хороший аспект, но с примерами сценариев, информация, надеюсь, усвоится лучше. Ниже я приведу несколько примеров:
1. Каскадные задачи с задержкой и проверкой статуса
# Основная задача в 2:00
0 2 * * * /path/to/first_task.sh && touch /tmp/first_success
# Вторая задача через 15 минут, только если первая успешна
15 2 * * * [ -f /tmp/first_success ] && /path/to/second_task.sh && rm /tmp/first_success
first_task.sh
запускается в 2:00, и если успешно (&&
), создаётся файл-флаг/tmp/first_success
В 2:15 проверяется наличие флага (
[ -f ... ]
), и если он есть, запускаетсяsecond_task.sh
, после чего флаг удаляетсяПотенциальное применение: цепочки задач (например, сбор данных - обработка - отправка отчёта)
2. Запуск только при низкой загрузке CPU
0 22 * * * [ $(awk '{print 100-$NF}' /proc/loadavg | cut -d. -f1) -lt 30 ] && /path/to/backup.sh
awk '{print 100-$NF}' /proc/loadavg
- вычисляет свободный CPU (100 — загрузка)cut -d. -f1
- оставляет только целую часть-lt 30
- проверяет, что свободно больше 70% (загрузка < 30%)Потенциальное применение: тяжёлые задачи (анализ логов, бэкапы).
3. Пропуск запуска, если процесс уже выполняется
*/5 * * * * [ $(pgrep -c -f "script.sh") -eq 0 ] && /path/to/script.sh
pgrep -c -f "script.sh"
- подсчитывает количество запущенных процессов-eq 0
- если процесс не найден (0
), задача запускаетсяПотенциальное применение: предотвращение дублирующихся задач (например, импорт данных).
4. Ротация логов
0 3 1 * * find /var/log/app/ -name "*.log" -mtime +30 -exec gzip {} \;
find /var/log/app/
- ищет файлы.log
старше 30 дней-exec gzip {} \;
- сжимает их
5. Ограничение времени выполнения скрипта
0 * * * * timeout 600 /path/to/long_script.sh
timeout 600
- убивает процесс через 600 секунд (10 минут).
Альтернатива cron
Возможно, в момент прочтения блоков выше, были вопросы из-за наличия Systemd Timers, как наилучшей альтернативе cron
. Как и в любой тематике нашей сферы, аналоги есть буквально у всего.
Я поведаю о нём кратко, тк статья немного не про него. Ниже - ключевые особенности и сравнение.
1. Ключевые особенности
Точное расписание, а именно - запуск по календарю (
OnCalendar=*-*-* 03:00:00
— ежедневно в 3:00) и относительные интервалы (OnBootSec=5min
- через 5 минут после старта системы).Зависимости. К примеру, можно указать зависимость от других сервисов (старт задачи после запуска сети).
Устойчивость к пропускам. Если сервер был отключен - таймер не запустит задачу при следующем включении (
Persistent=true
).Интеграция с
systemd
. Логирование вjournalctl
, управление командами:
systemctl start mytimer.timer # Запустить таймер
journalctl -u mytimer.service # Посмотреть логи
2. Сравнение
Возможность | Systemd Timer | Cron |
---|---|---|
Зависимости | Да ( | Нет |
Запуск после пропуска | Да ( | Нет |
Логирование |
| Файлы (/var/log/) |
Точное время | Секунды | Только минуты |
Заключение
Какой вывод можно сделать из статьи? Cron
- простой и полезный инструмент для автоматизации задач, но при не самом аккуратном использовании может "подкинуть" Вам проблем: от падения сервера из-за перегрузки до "тихих" ошибок, о которых можно долго не знать. Самые важные правила при его использовании:
Всегда указывать полные пути
По возможности контролировать параллельное выполнение
Обязательно настраивать логирование (это, наверное, для всей ИТ-сферы применимо. Не мне учить, так сказать)
Для сложных сценариев, где обязательны зависимости между задачами или более точное время выполнения - безусловно systemd.timer
будет более приоритетным, тк даёт в разы больше контроля и лучше интегрируется. В конечном итоге, выбор между cron
и альтернативами напрямую зависит от задач. Что-то простое? Используем cron
. Необходима гибкость и надёжность? Systemd timers незаменим.
P.S. Я веду свою группу в Телеграмм, буду рад видеть всех, кому интересен процесс написания скриптов и автоматизация в мире IT.