Вопрос или проблема
Я создаю cron-выражение, которое должно выполняться каждые 30 минут только в определённые рабочие дни, например: 6-й, 7-й и 8-й рабочие дни месяца.
В данный момент у меня есть cron-выражение, но оно выполняется только в 6-й, 7-й и 8-й дни месяца, независимо от того, являются ли эти дни рабочими или нет.
Текущее cron-выражение:
0 0/30 * 6-8 * ?
Я попробовал использовать выражение ближайшего рабочего дня:
0 0/30 * 6W * ?
Но оно не работает для диапазона дней – например, от 6W-8W.
Любая помощь в этом вопросе будет весьма признательна.
П.С. Я не хочу делать это с помощью скрипта.
Хм… Нет, вы не можете. Невозможно указать диапазон рабочих дней в одном cron-выражении. Cron-выражения ограничены указанием фиксированных дат или интервалов для полей дня месяца и дня недели.
Одно из решений – создать три отдельных задания cron, по одному для каждого рабочего дня, в который вы хотите выполнить команду, например:
# для 6-го рабочего дня месяца
0 0/30 * * * [ $(date +\%a -d "$(date +\%Y-\%m-01) + 5 рабочий день") = "Mon" ] && /path/to/your/command
# для 7-го рабочего дня месяца
0 0/30 * * * [ $(date +\%a -d "$(date +\%Y-\%m-01) + 6 рабочий день") = "Tue" ] && /path/to/your/command
# для 8-го рабочего дня месяца
0 0/30 * * * [ $(date +\%a -d "$(date +\%Y-\%m-01) + 7 рабочий день") = "Wed" ] && /path/to/your/command
Вы не можете (или, по крайней мере, я недостаточно умён, чтобы увидеть, как это сделать).
В crontab есть ограничения, которые позволяют вам выбирать день месяца и день недели.
Все рабочие дни будут днями недели 1-5, но этого недостаточно, потому что вам нужно считать или выражать
6-й, 7-й, 8-й день (дни недели: 1-5) в этом месяце
Поэтому вам нужен скрипт.
$ cat /home/jaroslav/tmp/workday-567.sh
#!/bin/bash
jan01() { date +%s -d `date +%Y-01-01`; }
december() { echo $(($(jan01) + 365*24*3600)); }
day=`jan01`; december=`december`;
today=${1:-$(date '+%Y-%m-%d')}
this_months_678th=$(
while [ $day -lt $december ];do
date '+%Y-%m-%d %B %A %u' -d@$day;
let day=$day+86400;
done |
sort -u |
sed -e "/[67]$/d; /$(date '+%Y-%m')/!d" |
sed -n -e '6p; 7p; 8p'
)
if echo -e "$this_months_678th" | grep -q $today; then
exit 0
fi
exit 1
Ноябрь 2018;
$ for i in 2018-11-{01..31};
do bash /home/jaroslav/tmp/workday-567.sh $i && echo run on $i;
done
run on 2018-11-08
run on 2018-11-09
run on 2018-11-12
Ответ или решение
Запустить задачу cron на 6-м, 7-м и 8-м рабочих днях месяца, используя только выражения cron, невозможно, так как резервирование дней основывается на фиксированных датах, а не на динамическом подсчете рабочих дней. Вместо этого, вы можете использовать три отдельных задания cron для каждой из работ, которые нужно выполнить.
Вот пример, как это можно организовать:
- Создайте три отдельные задачи cron, одну для каждого рабочего дня:
# Для 6-го рабочего дня месяца
0 */30 * * * [ $(date +\%u -d "$(date +\%Y-\%m-01) + 5 days" | grep -c '^[1-5]$') -eq 1 ] && /path/to/your/command
# Для 7-го рабочего дня месяца
0 */30 * * * [ $(date +\%u -d "$(date +\%Y-\%m-01) + 6 days" | grep -c '^[1-5]$') -eq 1 ] && /path/to/your/command
# Для 8-го рабочего дня месяца
0 */30 * * * [ $(date +\%u -d "$(date +\%Y-\%m-01) + 7 days" | grep -c '^[1-5]$') -eq 1 ] && /path/to/your/command
В этом случае используется команда date
для получения даты через определенное количество дней от первого числа месяца. Функция grep
фильтрует результаты, чтобы гарантировать, что это рабочий день (дни с понедельника по пятницу, отмеченные как 1-5).
- Если вам нужно более сложное решение, которое учитывает изменения в рабочем графике (например, праздничные дни), можно создать bash-скрипт, который будет вычислять рабочие дни. Вот пример такого скрипта:
Создайте файл workdays.sh
:
#!/bin/bash
# Функция для получения n-го рабочего дня месяца
get_workday() {
local month=$1
local year=$2
local n=$3
local count=0
local day=1
# Увеличиваем день, пока не найдем n-ный рабочий день
while [ $count -lt $n ]; do
if [ $(date -d "$year-$month-$day" +%u) -lt 6 ]; then
((count++))
fi
((day++))
done
# Возвращаем n-ый рабочий день
echo $((day - 1))
}
# Проверяем текущую дату
today=$(date +%Y-%m-%d)
current_day=$(date +%d)
current_month=$(date +%m)
current_year=$(date +%Y)
# Определяем 6-й, 7-й и 8-й рабочие дни
sixth_workday=$(get_workday $current_month $current_year 6)
seventh_workday=$(get_workday $current_month $current_year 7)
eighth_workday=$(get_workday $current_month $current_year 8)
# Запускаем команду, если сегодня один из нужных рабочих дней
if [[ $current_day -eq $sixth_workday || $current_day -eq $seventh_workday || $current_day -eq $eighth_workday ]]; then
/path/to/your/command
fi
Затем добавьте в crontab вызов этого скрипта каждые 30 минут:
*/30 * * * * /path/to/workdays.sh
Таким образом, используя такие решения, вы сможете организовать выполнение команд только в рабочие дни, которые вам необходимы.