Como posso dizer ao cron para executar um comando em dias alternados (ímpar / par)

45

Ao configurar o cron para executar um comando em dias alternados, usando o campo "Dia do mês", da seguinte maneira:

1 22 */2 * * COMMAND

é executado sempre que o dia do mês é ímpar: 1,3,5,7,9 e assim por diante.

Como posso configurar o cron para rodar em dias do mês iguais a 2,6,8,10 e assim por diante (sem especificá-lo literalmente, o que é problemático, pois todo mês tem um número diferente de dias no mês)?

Freddie
fonte

Respostas:

60

A sintaxe que você tentou é realmente ambígua. Dependendo de quantos dias há no mês, alguns meses serão executados em dias ímpares e outros em pares. Isso ocorre porque a maneira como é calculada pega o número total de possibilidades e as divide. Você pode substituir esse comportamento de estratificação, especificando manualmente o intervalo de dias e usando um número ímpar ou par de dias. Como os scripts de dia par nunca seriam executados no 31º dia de meses mais longos, você não perde nada usando 30 dias como base para dias pares, e especificando especificamente para dividi-lo como se houvesse 31 dias, você pode forçar impares dias de execução.

A sintaxe ficaria assim:

# Will only run on odd days:
0 0 1-31/2 * * command

# Will only run on even days:
0 0 2-30/2 * * command

Sua preocupação com os meses que não têm o mesmo número de dias não é importante aqui, porque não há meses com mais dias do que isso e, para fevereiro ruim, o período não coincide com o último dia ou dois, mas não fará mal a ninguém. listado.

A única 'pegadinha' dessa abordagem é que, se você estiver em um ciclo de dias ímpares, após meses com 31 dias, seu comando também será executado no primeiro dia do mês. Da mesma forma, se você estiver forçando um ciclo uniforme, cada ano bissexto causará um ciclo de três dias e o final de fevereiro. Você realmente não pode contornar o fato de que qualquer padrão regular de "todos os dias" nem sempre cai em dias pares ou ímpares todos os meses e, de qualquer forma que forçar isso, você terá uma corrida extra ou perderá uma corrida entre meses com contagem incorreta de dias.

Caleb
fonte
1
Obrigado, mas o que acontecerá em meses como fevereiro, onde você tem apenas 28 dias? A estrela realmente cuida disso - mas é realmente ambígua.
Freddie
3
@ freddie: Veja minha resposta editada ... mas não é um problema, porque os valores fora do intervalo serão ignorados, nada acontecerá nos dias 30 ou 31 de fevereiro. Sempre. Você pode especificar manualmente com uma lista como 0,2,4...,30,32,34e isso não importa, os valores fora do intervalo nunca serão correspondidos.
Caleb
1
Obrigado! Entendo, obrigado pela resposta informativa!
Freddie
3
No servidor Ubuntu 8.04, parece que a sintaxe usando o dia zero do dia é inválida (dia ruim do mês). A seguinte sintaxe, no entanto, é aceita:0 0 2-30/2 * * command
1
O Fedora e o RHEL 5,6,7 também não gostam de 0 como um dia do mês. Como user31053 apontou: 0 0 2-30/2 * * commandfunciona como esperado.
NoelProf
2

Eu acho que uma possibilidade é usar o dia do ano, assim:

# for odd days
test $(((`date +%j` % 2))) != 0 && command

# for even days
test $(((`date +%j` % 2))) == 0 && command

É testado para sistemas Unix e Linux.

Jordi
fonte
Prefiro esta resposta porque ignora o problema com o número ímpar de dias com alguns meses. De qualquer forma, sugiro a notação dólar em vez backticks:test $(($(date +%j) % 2)) == 0 && command
caligari
Eu revi que% j não é a data Julian, então o melhor código evitando a transição Ano Novo deve ser calculado com segundos:test $(($(date +%s) / 86400 % 2)) == 0 && command
caligari
Obrigado pelos comentários! Nossa proposta funcionou para nós como um encanto. Usamos esse esquema para executar crons diários em diferentes nós do servidor, todos compartilhando o mesmo crontab, mas o script permite apenas executar o script em um deles. No entanto, se precisarmos torná-lo mais específico, consideraremos sua proposta. Obrigado!!!
Jordi
1

Vamos verificar todos os dias se é um "outro" :-) ( bcprograma obrigatório)

0 0 * * * test $(echo `date +%s` / 86400 % 2 == 0 |bc) -eq 0 && command

(Não tenho certeza se o código aparece corretamente. A date +%sparte está entre as apóstrofes posteriores.)

user37264
fonte
Isso acontece todos os dias, mas não responde à pergunta! Ele ainda será executado algumas vezes em dias ímpares, outras em dias pares, dependendo do mês. Isso tem o mesmo resultado que o código da pergunta; você está chegando ao resultado fazendo suas próprias contas nos segundos desde a época. Isso ocorre em dias pares desde a época, mas não em dias pares do nosso calendário.
Caleb
1

Em geral, eu o executava todos os dias e o script usava a lógica para determinar se deveria ser executado hoje.

Criar um arquivo de status simples informando a última execução e comparando funcionaria com muita facilidade.

Se precisar ser executado por diferentes fontes, torne-o dependente de argumento.

Klas Mattsson
fonte