Como posso criar um trabalho cron que executa uma tarefa a cada três semanas?

9

Tenho uma tarefa que precisa ser executada no cronograma do meu projeto (3 semanas).

Sou capaz de configurar o cron para fazer isso toda semana ou (por exemplo) na 3ª semana de cada mês - mas não consigo encontrar uma maneira de fazer isso a cada três semanas.

Eu poderia hackear o script para criar arquivos temporários (ou similares) para que funcionasse na terceira vez em que foi executado - mas essa solução cheira.

Isso pode ser feito de maneira limpa?

itj
fonte
@itj 3 semanas = 21 dias, por que não colocar uma tarefa cron a cada 21 dias?
Studer
1
@studer Eu posso ter perdido alguma coisa, mas não achei que o crontab fosse tão flexível
itj

Respostas:

8

O arquivo crontab permite apenas especificar:

minute (0-59)
hour (0-23)
day of the month (1-31)
month of the year (1-12)
day of the week (0-6 with 0=Sunday)

Portanto, não é possível especificar quais semanas devem ser aplicadas.

Escrever um script de wrapper pode ser a melhor opção.
Você pode obter o número da semana em um shell-script usando

date +%U

ou

date +%V

dependendo se você deseja que suas semanas comecem no domingo ou na segunda-feira.
Então você poderia usar

week=$(date +%V)
check=$(( ($week - 1) % 3 ))

E $ check seria 0 na 1ª, 4ª, 7ª, ... semanas do ano.

njd
fonte
3
O que acontece no final do ano? Será executado mais horas ou pular uma semana ou algo assim, já que um ano não se divide igualmente em várias semanas?
davr 19/02/10
Obrigado pela resposta. Mais puro do que qualquer ideia que eu tive. É válido apenas por 1 ano - mas isso deve ser bom para a maioria dos projetos.
itj
Ao longo de vários anos, você terá duas execuções com espaçamento de 8 ou 9 dias entre o Natal e o 1º de janeiro, e é nesse momento que você precisará fazê-lo corretamente e registrar a "data da última execução" em algum lugar para calcular uma semana intervalo.
njd 19/02/10
3

Graças às respostas anteriores, foram apontadas as opções de época para data -e ou formatação como% s

Embora um pouco doloroso ((date +%s) / 86400)dê dias a partir da época.

Confiando no trabalho semanal que está sendo executado ao mesmo tempo, é fácil verificar isso em um dia específico de um período de três semanas ( $epoch_day%21 == 13por exemplo)

No meu caso, isso é bom, pois é uma tarefa de uma só vez. Se faltar no dia em particular, não haverá necessidade de correr na próxima oportunidade.

itj
fonte
1

Se você pode salvar um arquivo de carimbo de data / hora entre as execuções, pode verificar a data dele em vez de confiar apenas na data atual.

Se o seu comando find suporta valores fracionários para -mtime(ou possui -mmin) (o GNU find possui ambos , o POSIX parece não exigir nenhum dos dois ), você pode 'estrangular' os trabalhos do cron com find e touch .

Ou, se você possui um comando stat que suporta mostrar datas de arquivo como "segundos desde a época" (por exemplo, stat do Gnu coreutils , também outras implementações), você pode fazer sua própria comparação usando date , stat e os operadores de comparação do shell (junto com toque para atualizar um arquivo de carimbo de data / hora). Você também pode usar ls em vez de stat se ele puder fazer a formatação (por exemplo, ls do GNU fileutils ).

Abaixo está um programa Perl (eu o chamei n-hours-ago) que atualiza um arquivo de carimbo de data / hora e sai com êxito se o carimbo de data / hora original tiver idade suficiente. Seu texto de uso mostra como usá-lo em uma entrada crontab para acelerar um trabalho cron. Também descreve ajustes para “horário de verão” e como lidar com carimbos de data / hora 'atrasados' de execuções anteriores.

#!/usr/bin/perl
use warnings;
use strict;
sub usage {
    printf STDERR <<EOU, $0;
usage: %s <hours> <file>

    If entry at pathname <file> was modified at least <hours> hours
    ago, update its modification time and exit with an exit code of
    0. Otherwise exit with a non-zero exit code.

    This command can be used to throttle crontab entries to periods
    that are not directly supported by cron.

        34 2 * * * /path/to/n-hours-ago 502.9 /path/to/timestamp && command

    If the period between checks is more than one "day", you might
    want to decrease your <hours> by 1 to account for short "days"
    due "daylight savings". As long as you only attempt to run it at
    most once an hour the adjustment will not affect your schedule.

    If there is a chance that the last successful run might have
    been launched later "than usual" (maybe due to high system
    load), you might want to decrease your <hours> a bit more.
    Subtract 0.1 to account for up to 6m delay. Subtract 0.02 to
    account for up to 1m12s delay. If you want "every other day" you
    might use <hours> of 47.9 or 47.98 instead of 48.

    You will want to combine the two reductions to accomodate the
    situation where the previous successful run was delayed a bit,
    it occured before a "jump forward" event, and the current date
    is after the "jump forward" event.

EOU
}

if (@ARGV != 2) { usage; die "incorrect number of arguments" }
my $hours = shift;
my $file = shift;

if (-e $file) {
    exit 1 if ((-M $file) * 24 < $hours);
} else {
    open my $fh, '>', $file or die "unable to create $file";
    close $fh;
}
utime undef, undef, $file or die "unable to update timestamp of $file";
exit 0;
Chris Johnsen
fonte