Shell Scripting

Compte enrere del descans amb un script de Bash

/bin/bash


Un compte enrere al terminal és un exercici clàssic per practicar amb dates, aritmètica i bucles a Bash, i a més és útil: per controlar un descans, una pausa en una classe o el temps que falta per a una prova. En aquest article analitzem un script senzill que compta 15 minuts enrere, en veiem els problemes i el reescrivim de manera més robusta.


L’script original

#! /bin/bash

FIN_DESCANSO=`date --date='+15 minutes' -u +%s`
INICIO_DESCANSO=`date -u +%s`

let TIEMPO_DESCANSO=FIN_DESCANSO-INICIO_DESCANSO

for i in `seq 1 $TIEMPO_DESCANSO`
do
    HORA_ACTUAL=`date -u +%s`
    let TIEMPO_ACTUAL=FIN_DESCANSO-HORA_ACTUAL
    MINUTOS=`date -d @$TIEMPO_ACTUAL +%M`
    SEGUNDOS=`date -d @$TIEMPO_ACTUAL +%S`
    echo "Quedan" $MINUTOS:$SEGUNDOS "para el examen ..."
    sleep 1
done

Què fa, línia a línia:

  1. #! /bin/bash: el shebang, indica que l’script l’ha d’interpretar Bash.
  2. FIN_DESCANSO=…: calcula el moment en què acaba el descans. date --date='+15 minutes' dona la data d’aquí a 15 minuts i +%s la converteix en segons des de l’1 de gener de 1970 (l’epoch d’Unix).
  3. INICIO_DESCANSO=…: el moment actual, també en segons.
  4. let TIEMPO_DESCANSO=…: la durada del descans en segons (900).
  5. El bucle for recorre la llista de números que genera seq 1 $TIEMPO_DESCANSO: es repeteix 900 vegades.
  6. HORA_ACTUAL=… i let TIEMPO_ACTUAL=…: a cada volta, calcula quants segons queden.
  7. MINUTOS=… i SEGUNDOS=…: fa servir date -d @segons per convertir els segons que queden en minuts i segons.
  8. echo: mostra el temps que falta.
  9. sleep 1: espera un segon abans de la volta següent.

Problemes de l’original

Funciona en el cas habitual, però té diversos punts febles:

  • Fa servir la zona horària local per formatar el temps restant. date -d @300 +%M:%S no converteix «300 segons» en minuts i segons: interpreta 300 com un instant (les 00:05 de l’1 de gener de 1970 UTC) i el mostra en l’hora local. A Espanya els minuts surten bé perquè la diferència amb UTC són hores senceres, però en una zona com l’Índia (UTC+5:30) el resultat és erroni:
$ TZ=Europe/Madrid date -d @300 +%M:%S
05:00
$ TZ=Asia/Kolkata date -d @300 +%M:%S
35:00
  • Pot acabar mostrant un temps impossible. El bucle fa 900 voltes, però cada volta dura una mica més d’un segon (el sleep 1 més el temps d’executar date tres vegades). Al final, el temps restant es torna negatiu i date el mostra com si fos l’hora anterior a 1970:
$ date -u -d @-3 +%M:%S
59:57
  • No passa de 59 minuts. %M són els minuts d’una hora: un descans de 90 minuts es mostraria com a 30:00.
  • Sintaxi antiga: les cometes inverses (accents greus) i let funcionen, però avui s’escriu $(…) per a la substitució de comandes i $(( … )) o (( … )) per a l’aritmètica. L’opció -u en date +%s no serveix de res: els segons des de l’epoch són els mateixos en qualsevol zona horària.
  • Missatge incoherent: compta el temps de descans però diu «para el examen».

Versió millorada

#!/usr/bin/env bash
# compte_enrere.sh: compte enrere d'un descans (per defecte, 15 minuts)

minuts=${1:-15}
if [[ ! "$minuts" =~ ^[0-9]+$ ]]; then
    echo "Ús: $0 [MINUTS]" >&2
    exit 1
fi

fi_descans=$(( $(date +%s) + minuts * 60 ))

while true; do
    queden=$(( fi_descans - $(date +%s) ))
    (( queden <= 0 )) && break
    printf '\rQueden %02d:%02d per acabar el descans ' $(( queden / 60 )) $(( queden % 60 ))
    sleep 1
done

printf '\r%-40s\n' "S'ha acabat el descans. A la feina!"

Què canvia:

  • La durada és un argument: ./compte_enrere.sh compta 15 minuts i ./compte_enrere.sh 5, 5 minuts. L’expressió regular ^[0-9]+$ comprova que sigui un nombre enter.
  • Tota l’aritmètica es fa amb enters a Bash: els minuts són queden / 60 i els segons queden % 60 (el residu). No depèn de la zona horària i pot passar de 59 minuts.
  • El bucle és un while que s’atura quan el temps arriba a zero, sense comptar voltes. Si una volta s’endarrereix, el temps mostrat continua sent correcte perquè sempre es calcula a partir del rellotge.
  • printf amb %02d escriu sempre dues xifres (04:07), i el \r (retorn de carro) torna al principi de la línia, de manera que el compte enrere s’actualitza en una sola línia en lloc d’omplir la pantalla.

Així es veu mentre corre (la línia es va sobreescrivint cada segon):

$ ./compte_enrere.sh 5
Queden 04:58 per acabar el descans

I quan acaba:

$ ./compte_enrere.sh 5
S'ha acabat el descans. A la feina!

Idees per ampliar-lo

  • Avís sonor: afegeix printf '\a' al final perquè el terminal faci un so (si el té activat).
  • Notificació d’escriptori: en un escriptori amb el paquet libnotify-bin, notify-send "Descans" "S'ha acabat el descans" mostra un avís a la pantalla.
  • Formatar amb hores: per a durades llargues, calcula també queden / 3600 i mostra HH:MM:SS.

Amb unes poques línies, aquest script et permet practicar amb date, l’aritmètica de Bash, els bucles i printf, i de passada et dona una eina senzilla per controlar el temps.

Posts relacionats