Execute o script bash literalmente a cada 3 dias

8

Eu quero executar um script de shell literalmente a cada 3 dias. Usar o crontab com 01 00 */3 * * na verdade não atenderá à condição, porque ele seria executado no dia 31 e, novamente, no primeiro dia de um mês. A sintaxe */3 é o mesmo que dizer 1,4,7 ... 25,28,31.

Deve haver maneiras de fazer o script em si verificar as condições e sair se 3 dias não tiverem passado. Então o crontab executa o script todos os dias, mas o script em si verificaria se 3 dias se passaram.

Eu até encontrei algum código, mas ele me deu um 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)) {'
    
por Taavi 25.09.2016 / 14:36

5 respostas

12

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

Adicione estas linhas ao topo 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 como datefile= e adaptar o valor de seconds= às suas necessidades ( $((60*60*24*3)) é avaliado como 3 dias).

Se você não quiser um arquivo separado, também poderá armazenar o último tempo de execução no registro de data e hora da 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 à 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:

Novamente, não se esqueça de adaptar o valor de seconds= às suas necessidades ( $((60*60*24*3)) é avaliado como 3 dias).

    
por Byte Commander 25.09.2016 / 15:20
12

Cron é realmente a ferramenta errada para isso. Na verdade, existe uma ferramenta comumente usada e não suportada chamada em que pode funcionar. O principalmente é projetado para uso interativo e tenho certeza de que alguém encontraria uma maneira melhor de fazer isso.

No meu caso, eu tenho o script que estou executando listado em testjobs.txt e incluo 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 o echo para ter certeza de que tenho uma saída determinista e date para confirmar se o comando é executado conforme necessário. Quando em executa este comando, terminará adicionando um novo trabalho a pelo menos 3 dias. (Eu testei com um minuto - o que funciona)

Tenho certeza que serei chamado pela maneira que abusei, mas é uma ferramenta útil para agendar um comando para ser executado uma vez ou x dias depois de um comando anterior.

    
por Journeyman Geek 25.09.2016 / 16:06
3

Primeiro, o fragmento de código acima é uma sintaxe de Bash inválida, semelhante a Perl. Segundo, o parâmetro z para date faz com que ele exiba o fuso horário numérico. +%j é o número do dia. Você precisa de:

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

Mas você ainda vai 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 em manter uma contagem em um arquivo e testá-lo / atualizá-lo.

    
por waltinator 25.09.2016 / 15:18
2

Se você puder 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 executará o código e aguardará três dias antes de iniciar o loop novamente.

    
por mkingsbu 25.09.2016 / 16:25
2

Você pode usar anacron em vez de cron, ele é projetado exatamente para fazer o que você precisa. A partir do manpage:

Anacron can be used to execute commands periodically, with a frequency specified in days. Unlike cron(8), it does not assume that the machine is running continuously. Hence, it can be used on machines that aren’t running 24 hours a day, to control daily, weekly, and monthly jobs that are usually controlled by cron.

When executed, Anacron reads a list of jobs from a configuration file, normally /etc/anacrontab (see anacrontab(5)). This file contains the list of jobs that Anacron controls. Each job entry specifies a period in days, a delay in minutes, a unique job identifier, and a shell command.

For each job, Anacron checks whether this job has been executed in the last n days, where n is the period specified for that job. If not, Anacron runs the job’s shell command, after waiting for the number of minutes specified as the delay parameter.

After the command exits, Anacron records the date in a special timestamp file for that job, so it can know when to execute it again. Only the date is used for the time calculations. The hour is not used.

    
por Twinkles 26.09.2016 / 15:33