crontab в Linux: расписание задач, примеры и частые ошибки

опубликовано
читать
≈ 10 мин
автор
среда и версия
Debian 12, cron 3.0pl1-162, bash 5.2
содержание пройти тест Linux

cron — служба, которая запускает команды по расписанию, а crontab — таблица этого расписания: одна строка на задачу, пять полей времени и команда. crontab -e открывает твою таблицу, crontab -l показывает её. Задача из cron работает не в твоём терминале: у неё короткий PATH, /bin/sh вместо bash и нет экрана, поэтому команду, которая работает руками, в cron часто приходится записывать иначе.

Linux / 01

Пять полей времени, потом команда

Пять полей времени, потом командаСтрока */15 9-18 * * 1-5 /usr/local/bin/report.sh. Минута */15 — каждые 15 минут: 0, 15, 30 и 45. Час 9-18 — с 9 до 18 часов включительно. День месяца * — любой. Месяц * — любой. День недели 1-5 — с понедельника по пятницу. Команда /usr/local/bin/report.sh — её выполняет /bin/sh.*/15 9-18 * * 1-5 /usr/local/bin/report.shполезначениечто значитминута*/15каждые 15 минут: :00, :15, :30, :45час9-18с 9 до 18 часов включительнодень месяца*любоймесяц*любойдень недели1-5с понедельника по пятницукоманда/usr/local/bin/report.shеё выполняет /bin/sh
Эта строка срабатывает каждые 15 минут с 9:00 до 18:45 по будням. Звёздочка значит «любое значение», дефис — диапазон, косая черта — шаг.

Как устроена строка crontab

Пять полей идут слева направо: минута, час, день месяца, месяц, день недели. Всё, что стоит после пятого поля, — команда, и выполняет её /bin/sh. Строка ниже запускает отчёт каждые 15 минут с 9:00 до 18:45 по будням:

*/15 9-18 * * 1-5 /usr/local/bin/report.sh

Задача срабатывает в ту минуту, когда текущее время подходит под поля. Исключение одно, и о нём ниже: если ограничены и день месяца, и день недели, хватает совпадения одного из них.

ПолеЗначенияПример и что он значит
минута0–59*/10 — каждые 10 минут
час0–239-18 — с 9 до 18 часов включительно
день месяца1–311,15 — 1-го и 15-го числа
месяц1–12 или jan–dec*/3 — январь, апрель, июль, октябрь
день недели0–7 или sun–sat1-5 — с понедельника по пятницу; 0 и 7 — воскресенье

Четыре знака покрывают почти все расписания. Звёздочка * значит «любое значение». Дефис задаёт диапазон, и он включительный: 8-11 в поле часов — это 8, 9, 10 и 11 часов. Запятая перечисляет значения: 0,30 в поле минут — в начале и в середине часа. Косая черта задаёт шаг: */2 в поле часов — каждый второй час, 0-30/10 в поле минут — на 0, 10, 20 и 30-й минуте.

Вместо пяти полей можно написать одно слово: @reboot — один раз при загрузке системы, @hourly — в начале каждого часа, @daily — в 0:00, @weekly — в 0:00 в воскресенье, @monthly — в 0:00 первого числа, @yearly — в 0:00 первого января.

Как открыть, посмотреть и удалить расписание

У каждого пользователя своя таблица. Работают с ней одной командой с разными ключами:

crontab -e      # открыть свою таблицу в редакторе
crontab -l      # показать её
crontab -r      # удалить целиком, без вопроса
crontab -i -r   # удалить, но сначала спросить

Пока таблицы нет, crontab -l так и отвечает и завершается с кодом 1:

crontab -l
no crontab for dev

Строку с ошибкой crontab не установит. Здесь полей времени четыре, поэтому echo попадает на место дня недели:

echo "* * * * echo hi" | crontab -
"-":0: bad day-of-week
errors in crontab file, can't install.

Ключ -r стоит на клавиатуре рядом с -e, а таблицу удаляет без подтверждения: после него crontab -l снова отвечает «no crontab for dev». С ключом -i команда сначала спрашивает:

crontab -i -r
crontab: really delete dev's crontab? (y/n)

Перед правками сохрани копию: crontab -l > ~/crontab.bak, вернуть её — crontab ~/crontab.bak. Эта же форма, crontab файл, заменяет таблицу целиком, а не дописывает строку. Чтобы добавить задачу без редактора и не потерять остальные, склей старую таблицу с новой строкой:

(crontab -l 2>/dev/null; echo "0 3 * * * /home/dev/bin/backup.sh") | crontab -
crontab -l | tail -3
* * * * * date +%H:%M >> /home/dev/logs/date-bad.log 2>&1
* * * * * date +\%H:\%M >> /home/dev/logs/date-ok.log 2>&1
0 3 * * * /home/dev/bin/backup.sh

Кроме пользовательских таблиц есть системные: /etc/crontab и файлы в каталоге /etc/cron.d/. В них между временем и командой стоит ещё одно поле — от чьего имени запускать:

# /etc/cron.d/cleanup — шестое поле: пользователь
0 4 * * * root /usr/local/bin/cleanup.sh

Каталоги /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly и /etc/cron.monthly ещё проще: скрипт, положенный туда с правом на выполнение, запускает run-parts раз в час, день, неделю или месяц.

Примеры расписаний

КогдаСтрока
каждую минуту* * * * *
каждые 5 минут*/5 * * * *
каждый час в 30 минут30 * * * *
каждый день в 3:000 3 * * *
по будням в 9:000 9 * * 1-5
по понедельникам в 8:3030 8 * * 1
1-го числа месяца в 0:000 0 1 * *
каждые 15 минут в рабочее время*/15 9-18 * * 1-5
один раз при загрузке системы@reboot

Проверить строку можно на crontab.guru: сайт пересказывает выражение словами и показывает ближайшие запуски. Время задач — это время сервера, а не твоего ноутбука. Если date на сервере печатает UTC, строка 0 9 * * * сработает в 9:00 по UTC, то есть в 12:00 по Москве.

Почему команда работает в терминале, но не в cron

Самая частая жалоба на cron: команда из терминала работает, а в расписании молчит. Причина в окружении. Чтобы увидеть, что получает задача, поставь в расписание её саму:

* * * * * env > /home/dev/logs/cron-env.txt

Через минуту в файле всё окружение задачи — пять переменных:

HOME=/home/dev
LOGNAME=dev
PATH=/usr/bin:/bin
SHELL=/bin/sh
PWD=/home/dev

В терминале у того же пользователя PATH в три раза длиннее, и первым в нём идёт ~/bin: Debian добавляет его в ~/.profile, если каталог существует.

echo $PATH
/home/dev/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

Вот как это выглядит на деле. Скрипт ~/bin/backup.sh печатает «backup done», и в таблице три задачи с ним:

* * * * * backup.sh
* * * * * /home/dev/bin/backup.sh
* * * * * /home/dev/bin/backup.sh >> /home/dev/logs/backup.log 2>&1

Первая задача не находит скрипт: в её PATH нет ~/bin. Вторая находит, но у файла нет права на выполнение. Третья падает так же, как вторая, только её ошибка остаётся в файле, а не уходит в почту. Вот что лежит в backup.log после трёх запусков, chmod +x ~/bin/backup.sh и ещё одного запуска:

/bin/sh: 1: /home/dev/bin/backup.sh: Permission denied
/bin/sh: 1: /home/dev/bin/backup.sh: Permission denied
/bin/sh: 1: /home/dev/bin/backup.sh: Permission denied
backup done

Голое backup.sh заработало, когда первой строкой таблицы встал PATH:

PATH=/home/dev/bin:/usr/bin:/bin
* * * * * backup.sh >> /home/dev/logs/bare.log 2>&1

Отсюда три правила. Команду и файлы указывай полным путём или задавай PATH в начале таблицы. Рабочий каталог задачи — домашний, PWD=/home/dev, поэтому относительные пути считаются от него. А то, что написано для bash, выноси в скрипт с первой строкой #!/bin/bash: саму строку таблицы выполняет /bin/sh, в Debian это dash.

Главная мысль: в cron нет твоего PATH, алиасов и .bashrc. Полный путь к команде и к файлам снимает большую часть проблем.

Знак % в команде

В строке crontab знак % служебный: man-страница предупреждает, что неэкранированный процент превращается в перевод строки, а всё после первого % уходит команде на стандартный ввод. Две задачи ниже отличаются только обратной косой чертой:

* * * * * date +%H:%M >> /home/dev/logs/date-bad.log 2>&1
* * * * * date +\%H:\%M >> /home/dev/logs/date-ok.log 2>&1

Журнал cron показывает, что от первой команды осталось date +: перенаправление в файл тоже ушло после процента, поэтому date-bad.log даже не появился.

server CRON[3689]: (dev) CMD (date +)
server CRON[3688]: (dev) CMD (date +%H:%M >> /home/dev/logs/date-ok.log 2>&1)

Во втором файле после двух запусков лежит то, что и задумано:

14:31
14:32

Куда уходит вывод и где смотреть журнал

В журнал cron пишет только факт запуска, а не результат. В Debian со службой rsyslog строки лежат в /var/log/syslog, на системах с journald их показывает journalctl -u cron:

grep CRON /var/log/syslog
2026-10-18T14:32:01.850562+00:00 server CRON[3715]: (dev) CMD (/home/dev/bin/backup.sh >> /home/dev/logs/backup.log 2>&1)
2026-10-18T14:32:01.850579+00:00 server CRON[3717]: (dev) CMD (/home/dev/bin/backup.sh)

Всё, что задача печатает в стандартный вывод и в поток ошибок, cron отправляет письмом владельцу таблицы. Тема письма — сама команда, тело — её вывод. Так в ящике /var/mail/dev выглядят письма о двух упавших задачах:

Subject: Cron <dev@server> backup.sh
/bin/sh: 1: backup.sh: not found

Subject: Cron <dev@server> /home/dev/bin/backup.sh
/bin/sh: 1: /home/dev/bin/backup.sh: Permission denied

Если почтового сервера на машине нет, вывод не сохраняется нигде, а в журнале остаётся одна строка:

server CRON[3919]: (CRON) info (No MTA installed, discarding output)

Поэтому вывод задач лучше сразу писать в файл: >> /home/dev/logs/backup.log 2>&1 дописывает в него и обычный вывод, и ошибки (подробнее — в разборе перенаправления). Строка MAILTO="" в начале таблицы отключает письма совсем.

День месяца и день недели: хватает одного совпадения

Если ограничены оба поля, cron не ищет их пересечение. В man-странице сказано прямо: команда запускается, «when either field matches the current time». Строка 30 4 1,15 * 5 сработает в 4:30 первого и пятнадцатого числа каждого месяца и ещё каждую пятницу, а не только в пятницы, выпавшие на эти числа. Чтобы запускать задачу, например, в первый понедельник месяца, ограничь одно поле, а второе условие проверь в самой команде — пример в частых вопросах.

Ошибки, на которых спотыкаются

Команда не найдена. В письме от cron — /bin/sh: 1: backup.sh: not found, хотя в терминале скрипт запускается. У задачи PATH=/usr/bin:/bin, а ~/bin и /usr/local/bin туда не входят. Пиши полный путь или задай PATH первой строкой таблицы.

Нет права на выполнение. Ошибка Permission denied для своего же скрипта значит, что у файла нет бита x. Помогает chmod +x (подробнее — в разборе прав доступа) или запуск через интерпретатор: /bin/sh /home/dev/bin/backup.sh.

Знак процента обрезал команду. Журнал показывает CMD (date +), а файла с выводом нет. Каждый % в строке таблицы экранируй обратной косой чертой.

Скрипт написан для bash. Строку таблицы выполняет /bin/sh, а в Debian это dash: [[ ... ]], source и массивы там не работают. Вынеси команды в скрипт с #!/bin/bash в первой строке или задай SHELL=/bin/bash в начале таблицы.

crontab -r вместо crontab -e. Таблица удаляется сразу, корзины нет. Держи копию через crontab -l > ~/crontab.bak или добавь в ~/.bashrc строку alias crontab='crontab -i'.

Таблицу заменили целиком. crontab файл и echo ... | crontab - ставят новую таблицу вместо старой. Чтобы добавить строку, склей её с выводом crontab -l.

Время сервера — не твоё время. На серверах часто стоит UTC. Проверь date и пересчитай: 9:00 по Москве при UTC на сервере — это 0 6 * * *.

Частые вопросы

Как запускать скрипт каждые 5 минут

Звёздочка со шагом в поле минут, полный путь и вывод в файл:

*/5 * * * * /home/dev/bin/backup.sh >> /home/dev/logs/backup.log 2>&1

Почему @reboot не сработал после перезапуска cron

@reboot выполняется один раз за загрузку системы, а не при каждом запуске службы. Debian отмечает первый старт файлом /run/crond.reboot и при перезапуске cron пропускает такие задачи. В журнале это видно:

(CRON) INFO (Running @reboot jobs)
(CRON) INFO (Skipping @reboot jobs -- not system startup)

Как проверить, что задача сработала

Строка CMD (...) в журнале значит только, что cron запустил команду. Узнать, чем она кончилась, можно по её собственному логу или по письму. Поэтому у каждой задачи стоит перенаправление >> файл 2>&1, и после первого запуска в файл стоит заглянуть.

Как добавить строку, не открывая редактор

Склей текущую таблицу с новой строкой и отдай результат crontab:

(crontab -l 2>/dev/null; echo "0 3 * * * /home/dev/bin/backup.sh") | crontab -

2>/dev/null прячет «no crontab for dev», если таблицы ещё нет.

Как запустить задачу в первый понедельник месяца

Ограничь день месяца первой неделей, а день недели проверь в команде: date +%u печатает номер дня недели, понедельник — 1. Процент в строке таблицы экранирован:

0 9 1-7 * * [ "$(date +\%u)" = 1 ] && /home/dev/bin/report.sh

В остальные дни первой недели проверка не проходит, и report.sh не запускается.

Где потренироваться

Проверить себя по Linux можно в тесте навыков: 8 вопросов, баллы видны без регистрации. Перед собеседованием пригодятся вопросы по Linux с ответами. Как остановить зависшую задачу и что значат коды возврата 143 и 137, разобрано в материале о процессах и сигналах. Если на сервере systemd, ту же задачу можно поручить таймеру: он пишет вывод в журнал и догоняет пропущенные запуски — как это настроить, показано в разборе systemctl и таймеров systemd.

Источники