Executar script bash literalmente após cada 3 dias


8

Eu quero executar um script de shell literalmente a cada 3 dias. O uso do crontab with 01 00 */3 * *na verdade não atende à condição, porque seria executado no dia 31 e, em seguida, novamente no 1º dia de um mês. A */3sintaxe é o mesmo que dizer 1,4,7 ... 25,28,31.

Deve haver maneiras de fazer o próprio script verificar as condições e sair se 3 dias não se passaram. Portanto, o crontab executa o script todos os dias, mas o próprio script verifica se 3 dias se passaram.

Eu até encontrei algum código, mas ele me deu erro de sintaxe, qualquer ajuda seria apreciada.

if (! (date("z") % 3)) {
     exit;
}

main.sh: line 1: syntax error near unexpected token `"z"'
main.sh: line 1: `if (! (date("z") % 3)) {'

4
O que você quer dizer exatamente? Como */3não funciona? "se 3 dias não se passaram": três dias desde o que? Por favor edite sua pergunta e esclarecer.
— terdon 25/09/16

4
literalmente literalmente? Parece uma pergunta x / y e você pode querer realmente falar sobre o que está tentando fazer. Você está tentando impedir que o crontab execute o script?
— Journeyman Geek

1
Editou a pergunta para explicar por que a solução crontab não funciona.
— Taavi

Algo assim date("z") % 3 == 0poderia sofrer de um problema semelhante: a condição será falsa pelos quatro dias entre 29 de dezembro e 3 de janeiro, a menos que esse mês de dezembro faça parte de um ano bissexto.
— Rhymoid

Respostas:


12

Para abortar e sair imediatamente de um script, se a última execução ainda não foi há pelo menos um tempo específico, você pode usar este método que requer um arquivo externo que armazene a data e hora da última execução.

Adicione estas linhas na parte superior do seu script Bash:

#!/bin/bash

# File that stores the last execution date in plain text:
datefile=/path/to/your/datefile

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Test if datefile exists and compare the difference between the stored date 
# and now with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test -f "$datefile" ; then
    if test "$(($(date "+%s")-$(date -f "$datefile" "+%s")))" -lt "$seconds" ; then
        echo "This script may not yet be started again."
        exit 1
    fi
fi

# Store the current date and time in datefile
date -R > "$datefile"

# Insert your normal script here:

Não se esqueça de definir um valor significativo datefile=e adaptar o valor seconds=às suas necessidades ( $((60*60*24*3))avalia em 3 dias).


Se você não quiser um arquivo separado, também poderá armazenar o último tempo de execução no carimbo de data / hora de modificação do seu script. Isso significa, no entanto, que fazer alterações no arquivo de script redefinirá o contador 3 e será tratado como se o script estivesse sendo executado com êxito.

Para implementar isso, adicione o snippet abaixo na parte superior do seu arquivo de script:

#!/bin/bash

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Compare the difference between this script's modification time stamp 
# and the current date with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test "$(($(date "+%s")-$(date -r "$0" "+%s")))" -lt "$seconds" ; then
    echo "This script may not yet be started again."
    exit 1
fi

# Store the current date as modification time stamp of this script file
touch -m -- "$0"

# Insert your normal script here:

Mais uma vez, não se esqueça de adaptar o valor seconds=às suas necessidades ( $((60*60*24*3))avalia em 3 dias).


Sim, gravar a última chamada bem-sucedida de um programa específico requer algum tipo de armazenamento de dados externo, e o sistema de arquivos é uma escolha óbvia para isso.
— 22716 Kilian Foth

Nem precisa armazenar a data - basta tocar no arquivo todas as vezes e verificar a hora.
— precisa saber é o seguinte

@ djsmiley2k Sim, você está certo. Se o risco de modificar o arquivo de script manualmente causar o atraso da redefinição for aceitável, também é possível usar o carimbo de data / hora da modificação. Eu adicionei isso à minha resposta.
— Byte Commander

Isso pode ser jogado um pouco, salvando o carimbo de data / hora atual no arquivo (em vez da data legível por humanos), para que você possa obter o tempo decorrido $[ $(date +%s) - $(< lastrun) ]. Se o script for executado a partir do cron uma vez por dia, posso adicionar uma folga ao intervalo de tempo necessário, para que, se a execução do script for atrasada em alguns segundos, a próxima vez não pulará um dia inteiro. Ou seja, verifique todos os dias se 71 horas passaram oslt.
— ilkkachu

Essa magia com o datefile realmente funcionou, muito obrigado!
— Taavi

12

Cron é realmente a ferramenta errada para isso. Na verdade, existe uma ferramenta comumente usada e pouco usada, na qual pode funcionar. at é projetado principalmente para uso interativo e tenho certeza de que alguém encontraria uma maneira melhor de fazer isso.

No meu caso, eu teria o script que estou executando listado em testjobs.txt e incluiria uma linha que lê.

Como exemplo, eu teria isso como testjobs.txt

echo "cat" >> foo.txt
date >> foo.txt
at now + 3 days < testjobs.txt

Eu tenho dois comandos inocentes, que podem ser seus shellscripts. Eu corro echo para garantir uma saída determinística e data para confirmar se o comando é executado conforme necessário. Quando at executa esse comando, ele termina adicionando um novo trabalho a por 3 dias. (Eu testei com um minuto - o que funciona)

Eu tenho certeza que eu vou ser chamado pela maneira que eu abusado pelo, mas é uma ferramenta útil para agendar um comando a ser executado em um momento ou x dias após um comando anterior.


3
+1 como atparece a melhor abordagem aqui. O problema dessa abordagem é que, sempre que você executa o script manualmente, adiciona outro conjunto de attarefas a cada três dias .
— Dewi Morgan

1
+1, mas outro problema (além do apontado por @DewiMorgan) é que, se um script falhar, todo o script subsequente não será iniciado (se at for após o ponto de falha), até que se perceba que o script foi falhou e reinicie-o. Isso pode ser ruim ou às vezes bom (ex: falha porque uma condição não está mais lá: é bom que ela não tente novamente em três dias?). E há um ligeiro desvio de cada vez (alguns milissegundos se atestá no topo, ou potencialmente muito mais se atestá perto do fundo de um roteiro de longa execução)
— Olivier Dulac

No pode enviar e-mails .... O que pode ser uma solução para falhas. atq e atrm permitiriam abater trabalhos errantes, talvez? Em teoria, você pode escrever uma data específica para at, mas isso parece deselegante e de mão.
— Journeyman Geek

Portanto, tenha um cronjob que verifique a existência da próxima execução em at; se não houver, adicione um por 3 dias e avise (ou execute-o imediatamente e verifique novamente).
— precisa saber é o seguinte

3

Primeiro, o fragmento de código acima é uma sintaxe inválida do Bash, se parece com Perl. Segundo, o zparâmetro para dategerar o fuso horário numérico. +%jé o número do dia. Você precisa:

if [[ ! $(( $(date +%j) % 3 )) ]] ;then
     exit
fi

Mas você ainda verá estranheza no final do ano:

$ for i in 364 365  1 ; do echo "Day $i, $(( $i % 3 ))"; done
Day 364, 1
Day 365, 2
Day 1, 1

Você pode ter mais sorte mantendo uma contagem em um arquivo e testando / atualizando isso.


1
Perl não tem uma date()função interna, mas que parece um pouco com date () em PHP (onde zé "o dia do ano (a partir de 0)")
— ilkkachu

2

Se você puder simplesmente deixar o script em execução perpetuamente, poderá fazer:

while true; do

[inert code here]

sleep 259200
done

Esse loop é sempre verdadeiro, portanto, ele sempre executa o código e aguarda três dias antes de iniciar o loop novamente.


Por que não while true?
— Jonathan Leffler

Hah! Boa pegada. Eu não precisava usar isso em um longo tempo, eu esqueci que existia lol
— mkingsbu

2
Como atsolução, isso varia conforme o tempo de execução do script a cada execução. É claro que isso pode ser contornado, economizando o tempo em que o script é iniciado e adormecendo até três dias depois.
— ilkkachu

2

Você pode usar o anacron em vez do cron, ele foi projetado exatamente para fazer o que você precisa. Na página de manual:

O Anacron pode ser usado para executar comandos periodicamente, com uma frequência especificada em dias. Diferentemente do cron (8), ele não assume que a máquina esteja funcionando continuamente. Portanto, ele pode ser usado em máquinas que não estão funcionando 24 horas por dia, para controlar tarefas diárias, semanais e mensais que geralmente são controladas pelo cron.

Quando executado, o Anacron lê uma lista de trabalhos de um arquivo de configuração, normalmente / etc / anacrontab (consulte anacrontab (5)). Este arquivo contém a lista de trabalhos que a Anacron controla. Cada entrada de trabalho especifica um período em dias, um atraso em minutos, um identificador de trabalho exclusivo e um comando shell.

Para cada trabalho, a Anacron verifica se esse trabalho foi executado nos últimos n dias, em que n é o período especificado para esse trabalho. Caso contrário, o Anacron executa o comando shell do trabalho, depois de aguardar o número de minutos especificado como o parâmetro delay.

Após a saída do comando, o Anacron registra a data em um arquivo de carimbo de data / hora especial para esse trabalho, para que ele saiba quando executá-lo novamente. Somente a data é usada para os cálculos de tempo. A hora não é usada.


Bom, certamente testarei o Anacron. Admira por isso que é tão subestimado se ele pode fazer tal mágica
— Taavi

A verdadeira questão: por que o cron não foi substituído por algo melhor agora? O fcron existe e acho que o systemd está trabalhando em sua própria solução, mas não estou atualizado com o estado atual das coisas.
— Twinkles
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.