Робота CRON виконується в останній день місяця


95

Мені потрібно створити роботу CRON, яка працюватиме в останній день кожного місяця. Я буду створювати його за допомогою cPanel.

Будь-яка допомога вдячна. Дякую

Відповіді:


180

Можливо, найпростіший спосіб - це просто зробити три окремі роботи:

55 23 30 4,6,9,11        * myjob.sh
55 23 31 1,3,5,7,8,10,12 * myjob.sh
55 23 28 2               * myjob.sh

Це діятиме 28 лютого, хоча навіть у високосний рік, тому, якщо це проблема, вам потрібно буде знайти інший спосіб.


Однак, як істотно легше, так і правильніше виконувати роботу якомога швидше в перший день кожного місяця, десь на зразок:

0 0 1 * * myjob.sh

та модифікуйте сценарій для обробки даних попереднього місяця.

Це усуває будь-які клопоти, з якими ви можете зіткнутися з з’ясуванням того, який день є останнім у місяці, а також гарантує доступність усіх даних за цей місяць, за умови, що ви обробляєте дані. Запуск від п’яти хвилин до півночі останнього дня місяця може призвести до того, що ви пропустите все, що трапиться з того часу до півночі.

Це звичайний спосіб зробити це в будь-якому випадку для більшості робочих місць наприкінці місяця.


Якщо ви все ще дійсно хочете , щоб запустити його в останній день місяця, один з варіантів є просто виявити , якщо завтра буде перший (або як частина вашого сценарію, або в самому кронтаб).

Отже, щось на кшталт:

55 23 28-31 * * [[ "$(date --date=tomorrow +\%d)" == "01" ]] && myjob.sh

має бути хорошим початком, якщо ви маєте відносно розумну dateпрограму.

Якщо ваша dateпрограма недостатньо просунута, щоб дати вам відносні дати, ви можете просто скласти дуже просту програму, яка дасть вам завтрашній день місяця (вам не потрібна вся потужність date), наприклад:

#include <stdio.h>
#include <time.h>

int main (void) {
    // Get today, somewhere around midday (no DST issues).

    time_t noonish = time (0);
    struct tm *localtm = localtime (&noonish);
    localtm->tm_hour = 12;

    // Add one day (86,400 seconds).

    noonish = mktime (localtm) + 86400;
    localtm = localtime (&noonish);

    // Output just day of month.

    printf ("%d\n", localtm->tm_mday);

    return 0;
}

а потім використовуйте (припускаючи, що ви зателефонували tomdomза "завтрашній день місяця"):

55 23 28-31 * * [[ "$(tomdom)" == "1" ]] && myjob.sh

Хоча ви можете розглянути питання про включення перевірки помилок , так як time()і mktime()може повернутися , -1якщо що - то піде не так. У наведеному вище коді з міркувань простоти це не враховано.


7
так, може бути простіше бігати кожного першого дня, а не останнього дня :)
Utku Dalmaz

1
Дійсно 1-го числа місяця. Ось як виглядатиме код у PHP $ date = new DateTime ('01.03.2013'); $ date-> modify ('- 1 місяць'); $ previousMonth = $ date-> формат ('Y-m'); // $ previousMonth тепер 2013-02. Створіть запит для отримання продуктів за попередній місяць.
Lamy

Дані високосного року, 29 лютого, будуть втрачені, ми також повинні врахувати це. Відповідь Thunder Rabbit нижче вважає, що але cron пробіг двічі у
Hari Swaminathan

1
@Hari, кращим рішенням буде запуск першого числа місяця та збір даних попереднього місяця. У такому разі 29 лютого не пропустили б.
paxdiablo

1
Враховуючи високосні роки: і якщо вам не шкода, це буде працювати двічі: 55 23 28,29 2 * myjob.sh
radiantRazor

52

Існує дещо коротший метод, який можна використовувати, подібний до одного з наведених вище. Це є:

[ $(date -d +1day +%d) -eq 1 ] && echo "last day of month"

Крім того, запис crontab можна оновити, перевіряючи лише з 28 по 31 число, оскільки безглуздо запускати його в інші дні місяця. Що дасть вам:

0 23 28-31 * * [ $(date -d +1day +%d) -eq 1 ] && myscript.sh

Я не міг змусити це працювати як запис crontab (я думаю, щось потрібно уникати). Однак він відмінно працював у скрипті оболонки, викликаному з crontab. FYI, помилка, яку я отримав, була/bin/sh: -c: line 1: unexpected EOF while looking for matching ')' .
Марк Райчок

13
Це чудово працює. У файлі crontab% має бути екранованим. Отже[ $(date -d +1day +\%d) -eq 1 ] && run_job
ColinM

Справді приємний фокус! Але питання було позначене, posixі дата POSIX не підтримує "-d +1day": - \ Більш складним (і негарним) рішенням було б:[ `date +\%d` -eq `cal | xargs echo | awk '{print $NF}'` ] && myscript.sh
ckujau,

18

Налаштуйте завдання cron для запуску в перший день місяця. Потім змініть годинник системи на один день попереду.


10
що означає, що системний годинник буде постійно помилятися. Вибачте, але я думаю, що це спричинить більше проблем. -1 тоді.
Руді

69
Тепер я знаю, як почувався Галілей.
Том Андерсон,

7
@iconoclast: Я хотів би вважати це скоріше коаном, ніж жартом.
Том Андерсон,

14
Я думаю, що це дійсно погана І геніальна ідея. Не робіть цього вдома, діти.
Паскаль

11
@pascalbetz О, люди можуть це робити вдома, але їм справді не слід робити це на роботі.
Том Андерсон,

14

Що з цим, після Вікіпедії?

55 23 L * * /full/path/to/command

Ну що з цим? Це: "погані помилки дня місяця у файлі crontab, не вдається встановити. Хочете повторити те саме редагування?"
webjunkie

12
Щоб було зрозуміло, у цій статті Вікіпедії також згадується, що "L" нестандартне.
sdupton

10

Адаптуючи рішення paxdiablo, я бігаю 28 і 29 лютого. Дані 29 числа замінюють 28 числа.

# min  hr  date     month          dow
  55   23  31     1,3,5,7,8,10,12   * /path/monthly_copy_data.sh
  55   23  30     4,6,9,11          * /path/monthly_copy_data.sh
  55   23  28,29  2                 * /path/monthly_copy_data.sh

4
Якщо, звичайно, робота не має якогось руйнівного аспекту, такого як очищення всіх даних під час їх обробки :-)
paxdiablo

Це насправді безболісний варіант (найменш технічний), і вам може бути все одно, що тричі за 4 роки ви отримаєте cronjob занадто рано, якщо просто пропустите ,29.
Метт

@Matt: Ммм, ви не маєте на увазі, що він діятиме один день занадто рано один раз на чотири роки, якщо у вашому записі crontab буде сказано 55   23   28    2?
G-Man говорить "Поновити Моніку"

@ G-Man Так, ти маєш рацію, і крім цього, ти вже вдруге запускаєшся 29-го.
Метт,

8

Ви можете встановити завдання cron, яке запускатиметься кожного дня місяця, і запускатиме скрипт оболонки, як показано нижче. Цей сценарій визначає, чи менше число завтрашнього дня менше сьогоднішнього (тобто, якщо завтра новий місяць), а потім робить все, що завгодно.

TODAY=`date +%d`
TOMORROW=`date +%d -d "1 day"`

# See if tomorrow's day is less than today's
if [ $TOMORROW -lt $TODAY ]; then
echo "This is the last day of the month"
# Do stuff...
fi

Я не впевнений, що ви отримуєте, перевіряючи, чи менше це, ніж сьогодні - ви просто повинні мати можливість перевірити, чи це 1-й. Якщо перший день місяця не може бути 2-м або 3-м :-)
paxdiablo

7

Для більш безпечного методу в crontab на основі рішення @Indie (використання абсолютного шляху до date+ $()не працює у всіх системах crontab):

0 23 28-31 * * [ `/bin/date -d +1day +\%d` -eq 1 ] && myscript.sh

6

Деякі реалізації cron підтримують прапор "L", який представляє останній день місяця.

Якщо вам пощастить використовувати одну з цих реалізацій, це так просто, як:

0 55 23 L * ?

Це триватиме о 23:55 останнього дня кожного місяця.

http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger


5
#########################################################
# Memory Aid 
# environment    HOME=$HOME SHELL=$SHELL LOGNAME=$LOGNAME PATH=$PATH
#########################################################
#
# string         meaning
# ------         -------
# @reboot        Run once, at startup.
# @yearly        Run once a year, "0 0 1 1 *".
# @annually      (same as @yearly)
# @monthly       Run once a month, "0 0 1 * *".
# @weekly        Run once a week, "0 0 * * 0".
# @daily         Run once a day, "0 0 * * *".
# @midnight      (same as @daily)
# @hourly        Run once an hour, "0 * * * *".
#mm     hh      Mday    Mon     Dow     CMD # minute, hour, month-day month DayofW CMD
#........................................Minute of the hour
#|      .................................Hour in the day (0..23)
#|      |       .........................Day of month, 1..31 (mon,tue,wed)
#|      |       |       .................Month (1.12) Jan, Feb.. Dec
#|      |       |       |        ........day of the week 0-6  7==0
#|      |       |       |        |      |command to be executed
#V      V       V       V        V      V
*       *       28-31   *       *       [ `date -d +'1 day' +\%d` -eq 1 ] && echo "Tomorrow is the first today now is  `date`" >> ~/message
1       0       1       *       *       rm -f ~/message
*       *       28-31   *       *       [ `date -d +'1 day' +\%d` -eq 1 ] && echo "HOME=$HOME LOGNAME=$LOGNAME SHELL = $SHELL PATH=$PATH" 

5

Для реалізації AWS Cloudwatch cron (планування лямбда тощо) це працює:

55 23 L * ? *

Працює о 23:55 останнього дня кожного місяця.



3

Ви можете просто з'єднати всі відповіді в один рядок cron і використовувати лише dateкоманду.

Просто перевірте різницю між днем ​​місяця, який сьогодні є і буде завтра:

0 23 * * * root [ $(expr $(date +\%d -d '1 days') - $(date +\%d)  ) -le 0 ]  && echo true

Якщо ця різниця нижче 0, це означає, що ми міняємо місяць і є останній день місяця.


3
55 23 28-31 * * echo "[ $(date -d +1day +%d) -eq 1 ] && my.sh" | /bin/bash 

1
повторити команду та перевести її в bash - найкращий спосіб при роботі з cron. Оскільки cron використовує sh, а не bash. Перевірте, де ваш bash використовує "який bash". На FreeBSD це / usr / local / bin / bash, на Linux / bin / bash.
Дональд Дак,

2

Як що до цього?

редагувати .bashprofileдодавання користувачем:

export LAST_DAY_OF_MONTH=$(cal | awk '!/^$/{ print $NF }' | tail -1)

Потім додайте цей запис до crontab:

mm hh * * 1-7 [[ $(date +'%d') -eq $LAST_DAY_OF_MONTH ]] && /absolutepath/myscript.sh

0

Останній день місяця може бути 28-31 залежно від того, який це місяць (лютий, березень тощо). Однак у будь-якому з цих випадків наступний день завжди є 1 числа наступного місяця. Отже, ми можемо використовувати це, щоб переконатися, що ми виконуємо якусь роботу завжди в останній день місяця, використовуючи код нижче:

0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.