Как настроить crontab на выполнение 6, 7 и 8 рабочих дней месяца?

Вопрос или проблема

Я создаю 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 для каждой из работ, которые нужно выполнить.

Вот пример, как это можно организовать:

  1. Создайте три отдельные задачи 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).

  1. Если вам нужно более сложное решение, которое учитывает изменения в рабочем графике (например, праздничные дни), можно создать 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

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

Оцените материал
Добавить комментарий

Капча загружается...