Como executar um cron job a cada 36 horas?

0

Eu tenho me surpreendido durante alguns dias para fazer isso. (Não me pergunte por que eu quero fazer isso!)

Que tal adicionar dois trabalhos como este?

    00 06 current_day-31/3 * * job
    00 18 next_day-31/3 * * job
    
por Vineeth Vijayan 10.07.2015 / 07:19

2 respostas

2

Como @Rinzwind diz, você deve executar um script que deve verificar se é hora de executar, a cada 12 horas

0 0,6,12,18 * * * /path/to/script.sh

O problema é que, se você dividir por 36 diretamente, ele deve ser executado às 00 e 12 exatamente, e também irá pular o primeiro dia do ano.

Este script permite escolher valores arbitrários para ~ 00 e ~ 36 horas

#!/bin/bash

# Global variable with result from shouldItRun()
runCommand=0
# Run at this hours
runAt00=6
runAt36=18

# There's an issue when the year changes. Since ((365*24)/36) is
# not exact, so using the cron-only solution @JEL, it will run at
# 6PM on day 365 of first year, and at 6am on day 1 of second year  

# So ...
# Year when the script started running
yearStart=2015
# Get number of days since the first year the script is running
yearCurrent=$(('date '+%Y''-1))
daysAccumulatedUntilThisYear=0

for i in $(seq $yearStart $yearCurrent)
do
    thisYearDays=$(('date -d ''$i'-12-31' '+%j''+0))
    daysAccumulatedUntilThisYear=$(($daysAccumulatedUntilThisYear+$thisYearDays));
    echo '>> Year '$i' ('$thisYearDays') | Accumulated : '$daysAccumulatedUntilThisYear
done

function shouldItRun {
    # Init to false
    runCommand=0
    #
    hourOfMonth=$(($(($daysAccumulatedUntilThisYear+$1+0))*24))
    hourOfDay=$(($2+0))

    # POSIBLE VALUES 24,12,0
    hoursLeft=$(( $hourOfMonth % 36 ))

    # IF 24 hours left AND time = $runAt00:XX 
    if [ $hoursLeft -eq 24 ] && [ $hourOfDay -eq $runAt00 ]
    then
        echo '>>> DAY '$1' '$2':XX ('$hourOfMonth') : '$hoursLeft
        runCommand=1
    fi

    # IF 12 hours left AND time = $runAt36:XX
    if [ $hoursLeft -eq 12 ] && [ $hourOfDay -eq $runAt36 ]
    then
        echo '>>> DAY '$1' '$2':XX ('$hourOfMonth') : '$hoursLeft
        runCommand=1
    fi
}

# Example of today at this time
dayOfYear='date '+%j''
hourOfDay='date '+%H''

    #echo '> '$dayOfYear
    #echo '> '$hourOfDay

shouldItRun $dayOfYear $hourOfDay

if [ $runCommand -eq '1' ]
then
    echo 'Run it now!!'
else
    echo 'STOP : do not run it now!!'
fi


# Example with first 20 days of year, at 06: and 18:, 
# but tested also at 00: and 12:
for i in {1..20}
do
    shouldItRun $i 00
    shouldItRun $i 06
    shouldItRun $i 12
    shouldItRun $i 18    
done
    
por bistoco 10.07.2015 / 09:02
0

Por um único ano do calendário

Use duas linhas crontab. Saída de utilitário de data recebe o dia do ano (date + \% j) e a saída bc atribui um 0, 1 ou 2 ao dia com aritmética remanescente (o operador de módulo). Embora a configuração possa ser qualquer propagação de 36 horas, uma configuração de exemplo é executar o trabalho às 6h nos 1 dias, às 18h no dia seguinte, que será de 2 dias, e nos 0 dias não executará o trabalho. / p>

0 6 * * * jd='/bin/date +\%j'; /usr/bin/test '/bin/echo "$jd"\%3 | /usr/bin/bc' -eq 1 && job
0 18 * * *  jd='/bin/date +\%j'; /usr/bin/test '/bin/echo "$jd"\%3 | /usr/bin/bc' -eq 2 && job

Você pode testar isso na linha de comando com algo como

jd='/bin/date +\%j'; /usr/bin/test '/bin/echo "$jd"%3 | /usr/bin/bc' -eq 2 && echo 'yes'

Substitua jd por um dia de destino do ano e o 2 em "-eq 2" com 0 ou 1 para ver os resultados de um determinado dia. Por exemplo, em 2016, dias do ano 1-3 e 364-366:

1%3 = 1   364%3 = 1
2%3 = 2   365%3 = 2
3%3 = 0   366%3 = 0

Portanto, usando o cron, os utilitários de data e teste e bc como mostrado no exemplo acima, o trabalho será executado às 6h nos dias 1 e 364 de 2016, novamente às 18h nos dias 2 e 365 e não será executado nos dias 3 e 366.

Contabilidade da versão corrigida para anos sem saltos

Um comentarista generoso e outra resposta revelaram que a resposta acima falha para todos os anos, exceto um ano bissexto (2016, 2020). Isso ocorre porque o padrão remanescente não se mantém ao longo dos limites do ano após anos não bissextos (365 dias por ano). Portanto, o padrão restante precisa ser coagido em um padrão de 4 anos, em vez de um padrão de um ano.

O teste de linha de comando muito mais feio para o padrão de 4 anos é

jd='/bin/date +\%j';ya='/bin/date +\%Y';/usr/bin/test '/bin/echo if \("ya"%4\) 4-"ya"%4+"jd"%3 else "jd"%3 | /usr/bin/bc' -eq 0 && /bin/echo 'yes'

que, se o trabalho tiver que ser executado a cada 36 horas (por exemplo, como mostrado, às 6h de um dia, às 18h do dia seguinte e às 6h do dia seguinte) pode ser executado com duas linhas crontab conforme mostrado no exemplo único ano civil, isso é

0 6 * * * jd='/bin/date +\%j';ya='/bin/date +\%Y';/usr/bin/test '/bin/echo if \("ya"%4\) 4-"ya"%4+"jd"%3 else "jd"%3 | /usr/bin/bc' -eq 1 && job_to_run
0 18 * * * jd='/bin/date +\%j';ya='/bin/date +\%Y';/usr/bin/test '/bin/echo if \("ya"%4\) 4-"ya"%4+"jd"%3 else "jd"%3 | /usr/bin/bc' -eq 2 && job_to_run

Duas advertências merecem menção:

  1. Embora isso funcione com a versão stock bc na versão do Ubuntu que estou usando, a instrução else depende de extensões não POSIX para bc. Se essas extensões não POSIX não forem incorporadas em sua versão bc de ações, há uma solução alternativa, mas estou com preguiça de descobrir isso. Como isso é "Ask Ubuntu", eu me sinto seguro supondo que você esteja usando o Ubuntu Bc com as extensões não POSIX.
  2. Se o seu sistema estiver sobrecarregado com um fuso horário Daylight Savings, você precisará de 6 linhas crontab, cada uma para os intervalos de datas afetados pelo horário de verão, contabilizando o ganho e a perda de uma hora. Os intervalos de datas podem ser especificados com os campos cron.

Explicação narrativa

Assumo familiaridade com o uso de campos crontab (caso contrário, há sempre a página man). As duas linhas mostradas executarão "job" às 6h e 18h se a expressão de teste fornecida após os cinco campos de data e hora for bem-sucedida. A expressão de teste é bem sucedida da seguinte forma.

Vou explicar a expressão de teste mais complicada, porque a expressão mais simples é parte da expressão mais complicada.

  1. O utilitário de data é usado para preencher o dia do ano (1-366) e o ano (por exemplo, 2016) nas variáveis "jd" e "ya":
    jd='/bin/date +\%j';ya='/bin/date +\%Y'
  1. Em seguida, a saída de uma equação bc é testada (com / usr / bin / test). Se o resultado do cálculo de bc for igual a 1 ("-eq 1"), o trabalho será executado às 6h. Se o resultado do cálculo de bc for igual a 2, o trabalho será executado às 18h. Se o resultado do cálculo de bc for igual a qualquer outro número, incluindo 0, o trabalho não será executado.

  2. Em seguida, o utilitário echo é usado para enviar a equação bc para o utilitário bc.

  3. Primeiro, a equação bc é uma declaração "if". Se o resultado de bc avaliar a equação em parens após o "if", ("ya"%4) , for diferente de zero, a próxima equação, 4-"ya"%4+"jd"%3 , será avaliada; caso contrário, a equação após "else", "jd"%3 , será avaliada.

  4. Em todos os casos, "ya" é substituído pelo número do ano atual e "jd" é substituído pelo número do dia atual.

  5. Para a condição "if", o resultado é o restante após dividir o ano por 4. Para 2016, o restante é 0, então a equação "else" é calculada. Para 2017, o restante é 1, para 2018, o restante é 2 e, para 2019, o restante é 3. Portanto, para 2017-2019 (mas não para 2016 ou 2020), a equação 4-"ya"%4+"jd"%3 é calculada. No caso de 2017, o resultado de 4-2017% 4 é 3 (4-1); em 2018, o resultado é 2 e, em 2019, o resultado é 1. Esse resultado é adicionado ao número do dia antes de dividir o número do dia por 3 para obter o restante usado para testar se o trabalho deve ou não ser executado.

  6. Os padrões de 1,2,0 resultantes do cálculo testado de bc para os dias 1-3 e 363-365 em 2017-2019 são mostrados aqui:

    2017               2018              2019
    3 + 1 % 3 = 1      2 + 1 % 3 = 0     1 + 1 % 3 = 2
    3 + 2 % 3 = 2      2 + 2 % 3 = 1     1 + 2 % 3 = 0
    3 + 3 % 3 = 0      2 + 3 % 3 = 2     1 + 3 % 3 = 1

    3 + 363 % 3 = 0    2 + 363 % 3 = 2   1 + 363 % 3 = 1  
    3 + 364 % 3 = 1    2 + 364 % 3 = 0   1 + 364 % 3 = 2
    3 + 365 % 3 = 2    2 + 365 % 3 = 1   1 + 365 % 3 = 0
  1. Isso mostra que o padrão 0,1,2 no final de 2017 é repetido no início de 2018 e, do mesmo modo, 2,0,1 no final de 2018 e no início de 2019. O padrão mostrado acima em " Para um único ano civil "mostra que o padrão no final de 2016 e no início de 2020, 1,2,0, se repete no início de 2017 e no final de 2019.
por JEL 10.07.2015 / 08:36