Cómo (y por qué) evitar que un script Bash se reinicie demasiado pronto
Enlaces rápidos
- Toma un respiro
- Nuestra estrategia basada en el tiempo
- El tiempo en Linux
- Almacenamiento y recuperación de la hora
- Poniéndolo todo junto
- Útil en otros escenarios
A veces es útil asegurarse de que un script de shell Bash no se ejecute con demasiada frecuencia. A continuación se muestra una forma sencilla de establecer un límite de tiempo que debe expirar antes de que el script pueda ejecutarse nuevamente.
Toma un respiro
Dependiendo de lo que esté haciendo y de qué otros procesos pueda iniciar, un script Bash puede consumir tanta RAM y tiempo de CPU como cualquier otro proceso que consuma muchos recursos. Puede resultar útil limitar la frecuencia con la que se puede ejecutar dicho script.
Aplicar un período de respiro entre cada ejecución de un script pesado evita que acabe acaparando recursos. Los scripts computacionalmente costosos pueden monopolizar las computadoras hasta el punto de que otros usuarios experimenten una caída en el rendimiento. En casos extremos, si el script provoca una gran agitación del disco, por ejemplo, puede incluso acelerar la desaparición de su hardware.
Por supuesto, diseñar un esquema que limite la rapidez con la que se puede reiniciar un script agrega código a su script y le da una cosa más que hacer. Esto puede parecer contraproducente, pero si los controles son ligeros y rápidos, sus pequeños gastos generales se ven superados con creces por el ahorro de recursos.
Nuestra estrategia basada en el tiempo
Queremos imponer un período de tiempo mínimo que debe expirar antes de que se pueda repetir un script. Llamaremos a este período el intermedio. Lo definiremos como el tiempo entre la finalización de la ejecución anterior y el inicio de la nueva ejecución.
Necesitaremos guardar la hora en la que se completa el script, para que la próxima vez que se inicie podamos recuperarlo. Debido a que el script puede determinar fácilmente la hora actual, puede calcular la diferencia entre su hora de inicio y la hora de finalización del script anterior.
Si esa diferencia es menor que nuestro intervalo aceptable, el script se cerrará.
El tiempo en Linux
Linux ha estado contando los segundos desde la (segunda) época de Linux, que ocurrió a la medianoche del 1 de enero de 1970, UTC. Podemos ver la hora y la fecha emitiendo el comando de fecha.
date
Podemos pasar los especificadores de formato hasta la fecha para obtener el resultado en diferentes representaciones. Para ver el tiempo en segundos desde la época, use una 's' minúscula:
date +%s
Poder acceder al tiempo como un único número entero hace que sea muy fácil comparar dos tiempos y calcular el lapso de tiempo entre ellos. Eso es perfecto para nuestras necesidades y lo aprovecharemos bien.
Almacenamiento y recuperación de la hora
Podemos escribir fácilmente la hora en un archivo, simplemente redirigiendo la salida del comando de fecha. Podemos usar cat para verificar que funcionó.
date +%s > temp.dat
cat temp.dat
Eso nos da una manera de almacenar nuestra marca de tiempo. Tenga en cuenta que estamos usando un único > para la redirección, por lo que el archivo se recrea cada vez y solo contiene una entrada.
Dentro de nuestro script, necesitaremos abrir el archivo de marca de tiempo y leer el valor guardado. Ese valor debe mantenerse en una variable para que nuestro script pueda usarlo.
Parece bastante complicado, pero hay un truco sencillo que podemos utilizar. Dentro de nuestro script, usaremos el comando fuente para leer el archivo de marca de tiempo. Los comandos dentro del archivo fuente se ejecutan como si fueran comandos dentro de nuestro script.
Cuando guardamos la marca de tiempo, en realidad guardaremos un comando que crea una variable y le asigna el valor de tiempo. Cuando se obtiene el archivo, nuestro script ejecutará ese comando, creará la variable y almacenará el valor de tiempo en la variable, para que todo esté hecho por nosotros.
Podemos verificar ese proceso en la línea de comando. Redactamos y escribimos un comando en un archivo. El comando crea una variable llamada anterior_exit que se establece en la cantidad de segundos desde la época. Obtenemos el archivo. Luego comprobamos que ahora existe una variable llamada anterior_exit y vemos qué valor tiene.
echo "previous_exit=$(date +%s)" > timestamp.log
source timestamp.log
echo $previous_exit
Si examinamos el contenido del archivo, podemos verificar que el valor que tiene la variable es el que está en el archivo.
cat timestamp.log
Esa es una solución fácil y agradable para almacenar y recuperar nuestro valor de tiempo.
Poniéndolo todo junto
Repasemos los diferentes elementos del guión.
Mi script almacenará las marcas de tiempo en un archivo llamado .timestamp.log. Tenga en cuenta que el primer carácter es un punto ".", lo que significa que es un archivo oculto. Se almacenará en mi directorio de inicio.
El script crea una variable llamada timestamp_log para contener la ruta y el nombre del archivo.
A continuación, se define una función llamada set_timestamp. Cuando se llama, esta función escribe el valor que se le pasa en el archivo timestamp.log.
#!/bin/bash
# location of the timestamp log file
timestamp_log="/home/dave/.timestamp.log"
set_timestamp() {
echo "previous_exit=$1" > $timestamp_log
}
Debido a que el archivo timestamp.log se actualiza (y se crea si no existe) cuando el script sale, la primera vez que se ejecuta el script, el archivo timestamp.log no existirá. Eso causaría un problema cuando el script intente leerlo.
Para superar ese problema y protegerse contra situaciones en las que el archivo timestamp.log podría haberse eliminado, probamos la existencia del archivo. Si no existe, lo creamos almacenándole un valor de tiempo ficticio de cero.
# If the timestamp log file doesn't exist, create it
if [ ! -f $timestamp_log ]; then
set_timestamp 0
fi
Ahora podemos obtener el archivo de forma segura y leer las instrucciones que contiene. Esto establece la variable anterior_exit a la marca de tiempo anterior.
# get the last exit time as variable called previous_exit
source $timestamp_log
Ahora que tenemos el valor de la marca de tiempo, podemos calcular el período intermedio entre la marca de tiempo anterior y la hora actual.
# get the interim period since the last exit
interim=$(( $(date +%s)-$previous_exit ))
Ahora podemos realizar una prueba sencilla para ver si ha transcurrido suficiente tiempo para permitir la ejecución del script. Estoy usando un valor corto y arbitrario de cinco segundos para realizar pruebas.
if (( $interim <= 5 )); then
echo "Too soon... $interim seconds..."
exit 1;
fi
# your actual script starts here
echo "Running..."
Si el período intermedio dura más de cinco segundos, el guión puede continuar. Cuando se completa, escribimos la hora actual en el archivo timestamp.log llamando a nuestra función set_timestamp.
# set the new timestamp
set_timestamp $(date +%s)
exit 0
Aquí está el guión 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 esto en su editor favorito y guárdelo como un archivo llamado tc.sh. Recuerde cambiar el valor timestamp_log= en la línea 4 para que apunte a la ubicación en su computadora donde se debe almacenar timestamp.log.
Haga que su script sea ejecutable.
chmod +x tc.sh
Y ahora podemos ejecutarlo.
./tc.sh
Los intentos de ejecución posteriores dentro del período de exclusión de cinco segundos terminan automáticamente. Una vez transcurridos cinco segundos, podremos ejecutar nuevamente el script.
Útil en otros escenarios
Recuerde, si su secuencia de comandos tiene una ejecución ramificada y puede salir en diferentes puntos dentro de la secuencia de comandos, deberá llamar a set_timestamp antes de cada salida posible. Por eso valió la pena crear la función set_timestamp, aunque solo se usa dos veces en este script.
El truco de almacenar el nombre de la variable y su valor en un archivo de origen se puede aprovechar para leerlo en un archivo de configuración. Sólo necesitarás escribir una lista de nombres de variables y sus valores en un archivo y obtenerlo de tu script.