Trabalho CRON a ser executado no último dia do mês

95

Preciso criar um trabalho CRON que será executado no último dia de cada mês. Vou criá-lo usando cPanel.

Qualquer ajuda é apreciada. obrigado

Utku Dalmaz
fonte

Respostas:

180

Possivelmente, a maneira mais fácil é simplesmente fazer três trabalhos separados:

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

No entanto, isso acontecerá no dia 28 de fevereiro, mesmo em anos bissextos, então, se isso for um problema, você precisará encontrar outra maneira.


No entanto, geralmente é substancialmente mais fácil e correto executar o trabalho o mais rápido possível no primeiro dia de cada mês, com algo como:

0 0 1 * * myjob.sh

e modifique o script para processar os dados do mês anterior .

Isso elimina quaisquer dificuldades que você possa encontrar para descobrir qual dia é o último do mês e também garante que todos os dados desse mês estejam disponíveis, presumindo que você esteja processando dados. Se correr cinco minutos para a meia-noite do último dia do mês, você poderá perder tudo o que acontece entre essa hora e a meia-noite.

Essa é a maneira usual de fazer isso de qualquer maneira, para a maioria dos trabalhos de fim de mês.


Se você ainda realmente deseja executá-lo no último dia do mês, uma opção é simplesmente detectar se amanhã é o primeiro (seja como parte do seu script ou no próprio crontab).

Então, algo como:

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

deve ser um bom começo, supondo que você tenha um dateprograma relativamente inteligente .

Se o seu dateprograma não for avançado o suficiente para fornecer datas relativas, você pode simplesmente montar um programa muito simples para fornecer o dia do mês de amanhã (você não precisa de todo o poder de date), como:

#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;
}

e então use (supondo que você o tenha chamado tomdomde "dia do mês de amanhã"):

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

Embora você possa considerar a adição de verificação de erros, pois ambos time()e mktime()pode retornar -1se algo der errado. O código acima, por uma questão de simplicidade, não leva isso em consideração.

paxdiablo
fonte
7
sim, pode ser mais fácil correr no primeiro dia em vez do último :)
Utku Dalmaz
1
Primeiro dia do mês, de fato. Veja como o código ficará em PHP $ date = new DateTime ('2013-03-01'); $ date-> modify ('- 1 mês'); $ previousMonth = $ date-> format ('Y-m'); // $ previousMonth agora é 2013/02. Crie uma consulta para buscar produtos do mês anterior.
Lamy
Os dados do ano bissexto, 29 de fevereiro, serão perdidos, também precisamos considerar isso. A resposta do Thunder Rabbit abaixo considera isso, mas o cron foi executado duas vezes em fevereiro do ano bissexto
Hari Swaminathan
1
@Hari, a solução preferida seria executar no primeiro dia do mês e coletar os dados do mês anterior. 29 de fevereiro não seria desperdiçado nesse caso.
paxdiablo
1
Considerando os anos bissextos: e se você não se importar em correr duas vezes: 55 23 28,29 2 * myjob.sh
radiantRazor
52

Existe um método ligeiramente mais curto que pode ser usado semelhante a um dos anteriores. Isso é:

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

Além disso, a entrada do crontab pode ser atualizada para verificar apenas no dia 28 ao 31, pois é inútil executá-lo nos outros dias do mês. O que lhe daria:

0 23 28-31 * * [ $(date -d +1day +%d) -eq 1 ] && myscript.sh
Indie
fonte
Não consegui fazer isso funcionar como uma entrada de crontab (acho que algo precisa ser escapado). No entanto, funcionou bem em um script de shell chamado de crontab. Para sua informação, o erro que recebi foi /bin/sh: -c: line 1: unexpected EOF while looking for matching ')'.
Mark Rajcok
13
Isso funciona muito bem. No arquivo crontab, o% deve ser escapado. Então[ $(date -d +1day +\%d) -eq 1 ] && run_job
ColinM
Belo truque, de fato! Mas a questão foi marcada posixe a data POSIX não suporta "-d +1day": - \ Uma solução mais complicada (e feia) seria:[ `date +\%d` -eq `cal | xargs echo | awk '{print $NF}'` ] && myscript.sh
ckujau
18

Configure um cron job para ser executado no primeiro dia do mês. Em seguida, altere o relógio do sistema para um dia adiantado.

Tom Anderson
fonte
10
o que significa que o relógio do sistema estará errado o tempo todo. Desculpe, mas acho que isso vai causar mais problemas. -1 então.
Rudy
69
Agora eu sei como Galileu se sentiu.
Tom Anderson
7
@iconoclast: Eu gostaria de pensar nisso mais como um koan do que como uma piada.
Tom Anderson
14
Acho que essa é uma ideia realmente ruim E brilhante. Não façam isso em casa, crianças.
Pascal
11
@pascalbetz Oh, as pessoas podem fazer isso em casa, mas não deveriam fazer no trabalho.
Tom Anderson de
14

Que tal este, depois da Wikipedia?

55 23 L * * /full/path/to/command
Piohen
fonte
Bem, e quanto a isso? Que: "erros de dia do mês ruins no arquivo crontab, não é possível instalar. Deseja repetir a mesma edição?"
webjunkie
12
Só para ficar claro, essa entrada da wikipedia também menciona que "L" não é padrão.
sdupton
10

Adaptando a solução do paxdiablo, corro nos dias 28 e 29 de fevereiro. Os dados do dia 29 substituem o dia 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
Coelho trovão
fonte
4
A menos, é claro, que o trabalho tenha algum aspecto destrutivo, como limpar todos os dados à medida que são processados ​​:-)
paxdiablo
Esta é na verdade uma opção indolor (menos técnica), e você pode não se importar se três vezes em 4 anos você receberá o cronjob muito cedo se simplesmente omitir o ,29.
Matt
@Matt: Umm, você não quer dizer que será executado um dia mais cedo uma vez a cada quatro anos se a sua entrada no crontab disser 55   23   28    2?
G-Man diz 'Reintegrar Monica' em
@ G-Man Sim, você está certo, e além disso, você executou uma segunda vez no dia 29.
Matt
8

Você pode configurar um cron job para ser executado todos os dias do mês e fazer com que ele execute um script de shell como o seguinte. Este script descobre se o número do dia de amanhã é menor que o de hoje (ou seja, se amanhã é um novo mês) e faz o que você quiser.

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
thomson_matt
fonte
Não tenho certeza do que você ganha ao verificar se é menor do que hoje - você deve apenas poder verificar se é o primeiro. A menos que o primeiro dia do mês possa ser o 2 ou 3 :-)
paxdiablo
7

Para um método mais seguro em um crontab baseado na solução @Indie (usar o caminho absoluto para date+ $()não funciona em todos os sistemas crontab):

0 23 28-31 * * [ `/bin/date -d +1day +\%d` -eq 1 ] && myscript.sh
zigarn
fonte
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" 
Leslie Satenstein
fonte
5

Para a implementação do AWS Cloudwatch cron (Scheduling Lambdas, etc.) isso funciona:

55 23 L * ? *

Funcionando às 23h55 do último dia de cada mês.

Dan Herman
fonte
4
00 23 * * * [[ $(date +'%d') -eq $(cal | awk '!/^$/{ print $NF }' | tail -1) ]] && job

Confira uma pergunta relacionada no fórum unix.com.

livrar
fonte
3

Você pode apenas conectar todas as respostas em uma linha cron e usar apenas o datecomando.

Basta verificar a diferença entre o dia do mês que é hoje e será amanhã:

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

Se esta diferença for inferior a 0 significa que alteramos o mês e existe o último dia do mês.

Lukasz Stelmach
fonte
3
55 23 28-31 * * echo "[ $(date -d +1day +%d) -eq 1 ] && my.sh" | /bin/bash 
Pato Donald
fonte
1
ecoar o comando e canalizá-lo para o bash é a melhor maneira de trabalhar com o cron. Já que o cron está usando sh e não bash. Verifique onde seu bash está usando 'which bash'. No FreeBSD é / usr / local / bin / bash, no Linux / bin / bash.
Pato Donald
2

Que tal isso?

editar a .bashprofileadição do usuário :

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

Em seguida, adicione esta entrada ao crontab:

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

O último dia do mês pode ser 28-31 dependendo do mês (fevereiro, março, etc.). No entanto, em qualquer um desses casos, o dia seguinte é sempre o primeiro dia do mês seguinte. Portanto, podemos usar isso para garantir que executamos algum trabalho sempre no último dia do mês, usando o código abaixo:

0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh
Rakesh Chintha
fonte