cron — служба, которая запускает команды по расписанию, а crontab — таблица этого расписания: одна строка на задачу, пять полей времени и команда. crontab -e открывает твою таблицу, crontab -l показывает её. Задача из cron работает не в твоём терминале: у неё короткий PATH, /bin/sh вместо bash и нет экрана, поэтому команду, которая работает руками, в cron часто приходится записывать иначе.
Пять полей времени, потом команда
Как устроена строка crontab
Пять полей идут слева направо: минута, час, день месяца, месяц, день недели. Всё, что стоит после пятого поля, — команда, и выполняет её /bin/sh. Строка ниже запускает отчёт каждые 15 минут с 9:00 до 18:45 по будням:
*/15 9-18 * * 1-5 /usr/local/bin/report.sh
Задача срабатывает в ту минуту, когда текущее время подходит под поля. Исключение одно, и о нём ниже: если ограничены и день месяца, и день недели, хватает совпадения одного из них.
| Поле | Значения | Пример и что он значит |
|---|---|---|
| минута | 0–59 | */10 — каждые 10 минут |
| час | 0–23 | 9-18 — с 9 до 18 часов включительно |
| день месяца | 1–31 | 1,15 — 1-го и 15-го числа |
| месяц | 1–12 или jan–dec | */3 — январь, апрель, июль, октябрь |
| день недели | 0–7 или sun–sat | 1-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:00 | 0 3 * * * |
| по будням в 9:00 | 0 9 * * 1-5 |
| по понедельникам в 8:30 | 30 8 * * 1 |
| 1-го числа месяца в 0:00 | 0 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.