Como (e por que) evitar que um script Bash seja reiniciado muito cedo
Links rápidos
- Respire fundo
- Nossa estratégia baseada no tempo
- Tempo no Linux
- Armazenando e recuperando o tempo
- Juntando tudo
- Útil em outros cenários
Às vezes é útil garantir que um script de shell Bash não seja executado com muita frequência. Esta é uma maneira interessante de definir um limite de tempo que deve expirar antes que o script possa ser executado novamente.
Respire fundo
Dependendo do que está fazendo e de quais outros processos pode iniciar, um script Bash pode consumir tanta RAM e tempo de CPU quanto qualquer outro processo que consome muitos recursos. Pode ser útil limitar a frequência com que esse script pode ser iniciado.
Aplicar um período de descanso entre cada execução de um script pesado impede que ele consuma recursos. Scripts computacionalmente caros podem monopolizar os computadores a ponto de outros usuários experimentarem uma queda no desempenho. Em casos extremos, se o script causar muita agitação no disco, por exemplo, pode até acelerar o desaparecimento do seu hardware.
É claro que projetar um esquema que limita o tempo em que você pode reiniciar um script adiciona código ao seu script e oferece mais uma coisa para fazer. Isto pode parecer contraproducente, mas se as verificações forem leves e rápidas, as suas pequenas despesas gerais serão largamente compensadas pela poupança de recursos.
Nossa estratégia baseada no tempo
Queremos impor um período mínimo que deve expirar antes que um script possa ser repetido. Chamaremos esse período de provisório. Iremos defini-lo como o tempo entre a conclusão da execução anterior e o início da nova execução.
Precisaremos salvar o horário em que o script é concluído, para que na próxima vez que o script for iniciado possamos recuperá-lo. Como o script pode determinar facilmente a hora atual, ele pode calcular a diferença entre a hora de inicialização e a hora de término do script anterior.
Se essa diferença for menor que nosso intervalo aceitável, o script será encerrado.
Tempo no Linux
O Linux está contando os segundos desde a (segunda) época do Linux, que aconteceu à meia-noite de 1º de janeiro de 1970, UTC. Podemos ver a hora e a data emitindo o comando date.
date
Podemos passar especificadores de formato atualizados para obter a saída em diferentes renderizações. Para ver o tempo em segundos desde a época, use um ‘s’ minúsculo:
date +%s
Ser capaz de acessar o tempo como um único número inteiro torna muito fácil comparar dois tempos e descobrir o intervalo de tempo entre eles. Isso é perfeito para as nossas necessidades e faremos bom uso disso.
Armazenando e recuperando o tempo
Podemos facilmente escrever a hora em um arquivo, apenas redirecionando a saída do comando date. Podemos usar cat para verificar se funcionou.
date +%s > temp.dat
cat temp.dat
Isso nos dá uma maneira de armazenar nosso carimbo de data/hora. Observe que estamos usando um único > para o redirecionamento, portanto o arquivo é recriado a cada vez e contém apenas uma única entrada.
Dentro do nosso script, precisaremos abrir o arquivo de carimbo de data/hora e ler o valor salvo. Esse valor precisa ser mantido em uma variável para que nosso script possa utilizá-lo.
Isso parece bastante complicado, mas há um truque simples que podemos usar. Dentro do nosso script, usaremos o comando source para ler o arquivo de carimbo de data/hora. Os comandos dentro do arquivo de origem são executados como se fossem comandos dentro do nosso script.
Quando salvamos o carimbo de data/hora, na verdade salvaremos um comando que cria uma variável e atribui o valor de tempo a ela. Quando o arquivo for originado, nosso script executará esse comando, criará a variável e armazenará o valor do tempo na variável, então tudo será feito para nós.
Podemos verificar esse processo na linha de comando. Compomos e escrevemos um comando em um arquivo. O comando cria uma variável chamada previous_exit que é definida como o número de segundos desde a época. Nós fornecemos o arquivo. Em seguida, verificamos se uma variável chamada previous_exit existe agora e vemos qual valor ela contém.
echo "previous_exit=$(date +%s)" > timestamp.log
source timestamp.log
echo $previous_exit
Se examinarmos o conteúdo do arquivo, podemos verificar que o valor contido na variável é o que está no arquivo.
cat timestamp.log
Essa é uma solução fácil e agradável para armazenar e recuperar nosso valor de tempo.
Juntando tudo
Vamos examinar os diferentes elementos do script.
Meu script armazenará os carimbos de data/hora em um arquivo chamado .timestamp.log. Observe que o primeiro caractere é um ponto final ‘.’, o que significa que é um arquivo oculto. Ele será armazenado em meu diretório pessoal.
O script cria uma variável chamada timestamp_log para armazenar o caminho e o nome do arquivo.
A seguir, uma função chamada set_timestamp é definida. Quando é chamada, esta função grava o valor passado a ela no arquivo timestamp.log.
#!/bin/bash
# location of the timestamp log file
timestamp_log="/home/dave/.timestamp.log"
set_timestamp() {
echo "previous_exit=$1" > $timestamp_log
}
Como o arquivo timestamp.log é atualizado (e criado se não existir) quando o script sai, na primeira vez que o script for executado, o arquivo timestamp.log não existirá. Isso causaria um problema quando o script tentasse lê-lo.
Para superar esse problema e proteger contra situações em que o arquivo timestamp.log possa ter sido excluído, testamos a existência do arquivo. Se não existir, nós o criamos armazenando nele um valor de tempo fictício igual a zero.
# If the timestamp log file doesn't exist, create it
if [ ! -f $timestamp_log ]; then
set_timestamp 0
fi
Agora podemos obter o arquivo com segurança e ler as instruções dentro dele. Isso define a variável previous_exit para o carimbo de data/hora anterior.
# get the last exit time as variable called previous_exit
source $timestamp_log
Agora que temos o valor do carimbo de data/hora, podemos calcular o período intermediário entre o carimbo de data/hora anterior e a hora atual.
# get the interim period since the last exit
interim=$(( $(date +%s)-$previous_exit ))
Agora podemos realizar um teste simples para ver se passou tempo suficiente para que o script pudesse ser executado. Estou usando um valor arbitrário e curto de cinco segundos para teste.
if (( $interim <= 5 )); then
echo "Too soon... $interim seconds..."
exit 1;
fi
# your actual script starts here
echo "Running..."
Se o período intermediário for superior a cinco segundos, o script poderá prosseguir. Quando terminar, escrevemos a hora atual no arquivo timestamp.log chamando nossa função set_timestamp.
# set the new timestamp
set_timestamp $(date +%s)
exit 0
Aqui está o roteiro completo.
#!/bin/bash
# location of the timestamp log file
timestamp_log="/home/dave/.timestamp.log"
set_timestamp() {
echo "previous_exit=$1" > $timestamp_log
}
# If the timestamp log doesn't exist, create it
if [ ! -f $timestamp_log ]; then
set_timestamp 0
fi
# get the last exit time as a variable called previous_exit
source $timestamp_log
# get the interim period since the last exit
interim=$(( $(date +%s)-$previous_exit ))
if (( $interim <= 5 )); then
echo "Too soon... $interim seconds..."
exit 1;
fi
# set the new timestamp
set_timestamp $(date +%s)
echo "Running..."
exit 0
Copie isso em seu editor favorito e salve-o como um arquivo chamado tc.sh. Lembre-se de alterar o valor timestamp_log= na linha 4 para apontar para o local em seu computador onde o timestamp.log deve ser armazenado.
Torne seu script executável.
chmod +x tc.sh
E agora podemos executá-lo.
./tc.sh
As tentativas de execução subsequentes dentro do período de exclusão de cinco segundos serão encerradas automaticamente. Após cinco segundos, podemos executar o script novamente.
Útil em outros cenários
Lembre-se, se o seu script tiver execução ramificada e puder sair em pontos diferentes do script, você precisará chamar set_timestamp antes de cada saída possível. Por isso valeu a pena criar a função set_timestamp, mesmo que ela seja usada apenas duas vezes neste script.
O truque de armazenar o nome da variável e seu valor em um arquivo de origem pode ser aproveitado para ler um arquivo de configuração. Você só precisará escrever uma lista de nomes de variáveis e seus valores em um arquivo e obtê-la em seu script.