Dans un précédent article traitant de calendriers pèles-mêles, j’avais proposé l’écriture d’un programme cal permettant l’affichage vertical d’un mois.
Dans un souci d’apporter des solutions toujours meilleures, voici une nouvelle version de ce programme, qui apporte son lot d’améliorations :
- plus robuste
- plus performant
- plus pérenne et évolutif
Bref, que du bonheur pour un gourou linuxien !
Ici, il n’est plus question d’utiliser la commande cal… Le texte parsé : aux oubliettes ! A la place, nous allons utiliser la commande date. Cette dernière va avoir plusieurs rôles, à savoir le nom des jours à afficher dans notre calendrier vertical, et la détection de la fin du mois via son code d’erreur en sortie (récupéré grâce au « $? »).
Commençons par définir des variables simples, que nous pourrons remplacer par des paramètres « dynamiques » ultérieurement : nous fixons une année et un mois comme ceci :
Y=’2010′
m=’07’
Le reste du programme consiste ensuite en une boucle for, prenant comme paramètres les numéros de jours allant de 01 à 31. Une autre possibilité serait d’utiliser une boucle while en incrémentant une variable de 1 à 31, mais voilà, j’ai pas envie… C’est donc dans la boucle ci-dessous que nous allons mettre « tout » notre code ; autant dire pas grand chose, vous allez voir…
for d in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
do[…]
done
A ce stade, nous connaissons l’année « $Y », le numéro de mois « $m » et le numéro de jour « $d ». Il nous faut connaître le nom du jour. La commande date permet d’afficher le nom du jour de deux façons :
- avec « %A » pour l’affichage du nom complet (par exemple « lundi »)
- avec « %a » pour l’affichage du nom abrégé (par exemple « lun »)
A l’aide du paramètre « -d », nous allons pouvoir soumettre nos 31 dates à la-dite commande, qui va donc gentiment nous retourner les jours correspondant… lorsque ces dates existent, ou bien nous gueuler dessus dans le cas contraire. Cette faculté à rejeter une date non valide va justement nous intéresser dans la suite.
date +’%a’ -d "$Y-$m-$d"
L’exécution de la commande ci-dessus retourne :
- soit le nom abrégé du jour à la date précisée, ainsi que le code retour « 0 » pour « succès »
- soit un message d’erreur car la date est non valide, et le code retour « 1 » pour « échec »
Nous devons stocker dans une variable le nom du jour obtenu, et empêcher l’affichage d’un éventuel message d’erreur en le redirigeant vers « /dev/null ». Il ne nous reste plus qu’à tester le résultat du code retour ainsi :
- si c’est « 0 » (la date existe bien), on affiche le jour et son numéro dans le terminal
- si c’est « 1 » (la date est non valide), on sort de la boucle for, le programme s’arrête normalement
Voici le programme (presque !) complet :
function cal2 () { Y='2010' m='07' for d in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 do j=`date +'%a' -d "$Y-$m-$d" 2>> /dev/null` if test "$?" = 0 then printf "%3s %2s\n" "$j" "$d" else break fi done }
Le résultat est déjà pas mal, mais on peut faire plus pratique en retirant désormais les limitations de développement sur l’année et le numéro de mois. Si ces données sont passées en arguments, alors nous nous en servons. Dans le cas contraire, nous utiliserons la date actuelle.
test "$1" != "" && Y=$1 || Y=`date +’%Y’`
test "$2" != "" && m=$2 || m=`date +’%m’`
Pour s’assurer de la pertinence des arguments passés à notre fonction, nous allons les contrôler à l’aide des expressions régulières. Ainsi, en cas de valeur non valide, le programme est stoppé et retourne la valeur « 1 » sans rien écrire dans le terminal.
case $Y in [1-2][09][0-9][0-9]) ;; *) return 1 ; esac
case $m in [01][0-9]) ;; *) return 1 ; esac
Il ne nous reste qu’à ajouter ces quatre petites lignes dans notre fonction, et le tour est joué.
function cal2 () { test "$1" != "" && Y=$1 || Y=`date +'%Y'` test "$2" != "" && m=$2 || m=`date +'%m'` case $Y in [1-2][09][0-9][0-9]) ;; *) return 1 ; esac case $m in [01][0-9]) ;; *) return 1 ; esac for d in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 do j=`date +'%a' -d "$Y-$m-$d" 2>> /dev/null` if test "$?" = 0 then printf "%3s %2s\n" "$j" "$d" else break fi done }
Après exécution de notre nouvelle commande, voici le résultat dans le terminal :
[benoit:~]$ cal2 2010 07
jeu 01
ven 02
sam 03
dim 04
lun 05
mar 06
mer 07
jeu 08
ven 09
sam 10
dim 11
lun 12
mar 13
mer 14
jeu 15
ven 16
sam 17
dim 18
lun 19
mar 20
mer 21
jeu 22
ven 23
sam 24
dim 25
lun 26
mar 27
mer 28
jeu 29
ven 30
sam 31
Je vous épargnerai davantage de code, mais, pour une utilisation exclusivement en ligne de commande, on pourrait aussi ajouter quelques améliorations visuelles, comme par exemple afficher en rouge les dimanches, ou bien souligner la date du jour… Bref, de quoi jouer encore quelques heures sur un programme finalement peu utile !