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:
#! /bin/bash: el shebang, indica que l’script l’ha d’interpretar Bash.FIN_DESCANSO=…: calcula el moment en què acaba el descans.date --date='+15 minutes'dona la data d’aquí a 15 minuts i+%sla converteix en segons des de l’1 de gener de 1970 (l’epoch d’Unix).INICIO_DESCANSO=…: el moment actual, també en segons.let TIEMPO_DESCANSO=…: la durada del descans en segons (900).- El bucle
forrecorre la llista de números que generaseq 1 $TIEMPO_DESCANSO: es repeteix 900 vegades. HORA_ACTUAL=…ilet TIEMPO_ACTUAL=…: a cada volta, calcula quants segons queden.MINUTOS=…iSEGUNDOS=…: fa servirdate -d @segonsper convertir els segons que queden en minuts i segons.echo: mostra el temps que falta.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:%Sno 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 1més el temps d’executardatetres vegades). Al final, el temps restant es torna negatiu idateel mostra com si fos l’hora anterior a 1970:
$ date -u -d @-3 +%M:%S
59:57
- No passa de 59 minuts.
%Msó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
letfuncionen, però avui s’escriu$(…)per a la substitució de comandes i$(( … ))o(( … ))per a l’aritmètica. L’opció-uendate +%sno 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.shcompta 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 / 60i els segonsqueden % 60(el residu). No depèn de la zona horària i pot passar de 59 minuts. - El bucle és un
whileque 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. printfamb%02descriu 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 / 3600i mostraHH: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.