Chapitre 12. Programmation

Table des matières

12.1. Les scripts de l’interpréteur de commande
12.1.1. Compatibilité de l’interpréteur de commandes avec POSIX
12.1.2. Paramètres de l’interpréteur de commandes
12.1.3. Opérateurs conditionnels de l’interpréteur
12.1.4. Boucles de l’interpréteur de commandes
12.1.5. Séquence de traitement de la ligne de commandes de l’interpréteur
12.1.6. Programmes utilitaires pour les scripts de l’interpréteur de commandes
12.1.7. Dialogue de l’interpréteur de commandes
12.1.8. Exemple de script avec zenity
12.2. Make
12.3. C
12.3.1. Programme simple en C (gcc)
12.4. Déboguer
12.4.1. Exécution de base de gdb
12.4.2. Déboguer un paquet Debian
12.4.3. Obtenir une trace
12.4.4. Commandes avancées de gdb
12.4.5. Déboguer les erreurs de X
12.4.6. Vérifier les dépendances avec les bibliothèques
12.4.7. Outils de détection des fuites de mémoire
12.4.8. Outils d’analyse du code statique
12.4.9. Désassembler un binaire
12.5. Flex -- un meilleur Lex
12.6. Bison -- un meilleur Yacc
12.7. Autoconf
12.7.1. Compiler et installer un programme
12.7.2. Désinstaller un programme
12.8. Folie de courts scripts en Perl
12.9. Web
12.10. La conversion du code source
12.11. Créer un paquet Debian

Je donne quelques indications pour apprendre à programmer sous le système Debian, suffisantes pour suivre le code source mis en paquets. Voici les paquets importants correspondant aux paquets de documentation pour la programmations :

Tableau 12.1. Liste de paquets pour aider à la programmation

paquet popcon taille documentation
autoconf V:27, I:219 1898 « info autoconf » fourni par autoconf-doc
automake V:25, I:206 1699 « info automake » fourni par automake1.10-doc
bash V:845, I:999 5363 « info bash » fourni par bash-doc
bison V:15, I:127 2201 « info bison » fourni par bison-doc
cpp V:422, I:853 22 « info cpp » fourni par cpp-doc
ddd V:1, I:21 3628 « info ddd » fourni par ddd-doc
exuberant-ctags V:6, I:41 289 exuberant-ctags(1)
flex V:14, I:117 1288 « info flex » fourni par flex-doc
gawk V:275, I:326 1852 « info gawk » fourni par gawk-doc
gcc V:186, I:688 7 « info gcc » fourni par gcc-doc
gdb V:34, I:166 6300 « info gdb » fourni par gdb-doc
gettext V:56, I:358 6408 « info gettext » fourni par gettext-doc
gfortran V:8, I:63 2 « info gfortran » fourni par gfortran-doc (Fortran 95)
fpc I:5 42 fpc(1) et les pages html fournies par fp-doc (Pascal)
glade V:1, I:16 3152 l’aide fournie par le menu (UI Builder)
libc6 V:918, I:998 10280 « info libc » fourni par glibc-doc et glibc-doc-reference
make V:184, I:655 1291 « info make » fourni par make-doc
xutils-dev V:3, I:32 1432 imake(1), xmkmf(1), etc.
mawk V:554, I:997 198 mawk(1)
perl V:758, I:994 17566 perl(1) et les pages html fournies par perl-doc et perl-doc-html
python V:710, I:985 571 python(1) et les pages html fournies par python-doc
tcl8.4 V:17, I:202 167 tcl(3) et les pages de manuel détaillées fournies par tcl8.4-doc
tk8.4 V:11, I:135 168 tk(3) et les pages de manuel détaillées fournies par tk8.4-doc
ruby V:62, I:268 47 ruby(1) et la référence interactive fournir par ri
vim V:148, I:379 2063 menu d’aide (F1) fourni par vim-doc
susv2 I:0 39 fetch "The Single UNIX Specifications v2"
susv3 I:0 39 fetch "The Single UNIX Specifications v3"

Une référence en ligne est accessible en entrant « man name » après l’installation des paquets manpages et manpages-dev. Les références en ligne des outils GNU tools sont disponibles en entrant « info nom_programme » après l’installation des paquets de documentation pertinents. Vous devrez peut-être inclure les archives contrib et non-free en plus de l’archive main car certaines documentations GFDL ne sont pas considérées comme conformes à DFSG.

[Avertissement] Avertissement

N’utilisez pas « test » comme nom d’un fichier exécutable. « test » fait partie de l’interpréteur de commandes.

[Attention] Attention

Vous devrez installer les programmes directement compilés à partir des sources dans « /usr/local » ou « /opt » afin d’éviter des collisions avec les programmes du système.

[Astuce] Astuce

Les exemples de code pour la création de « Song 99 Bottles of Beer » devraient vous donner de bonnes indications sur pratiquement tous les langages de programmation.

Le script de l’interpréteur de commandes (« shell script » est un fichier texte dont le bit d’exécution est positionné et qui contient des commandes dans le format suivant :

#!/bin/sh
 ... lignes de commandes

La première ligne indique l’interpréteur qui sera utilisé pour lire et exécuter le contenu de ce fichier.

La lecture des scripts de l’interpréteur de commandes est la meilleure manière de comprendre comment fonctionne un système de type UNIX. Je donne ici quelques indications et rappels de la programmation avec l’interpréteur de commandes. Consultez « Erreurs en shell » (http://www.greenend.org.uk/rjk/2001/04/shell.html) pour apprendre à partir d’erreurs.

Contrairement à l’interpréteur de commandes en mode interactif (consultez Section 1.5, « La commande simple de l’interpréteur de commandes » et Section 1.6, « Traitement des données textuelles à la UNIX »), les scripts de l’interpréteur de commandes utilisent souvent des paramètres, des conditions et des boucles.

De nombreux scripts systèmes peuvent être interprétés par n’importe lequel des interpréteurs de commande POSIX (consultez Tableau 1.13, « Liste d’interpréteurs de commandes (« shells ») »). L’interpréteur de commandes par défaut pour le système est « /bin/sh » qui est un lien symbolique pointant vers le programme réel.

  • bash(1) pour lenny ou plus ancien

  • dash(1) pour squeeze ou plus récent

Évitez d’écrire des scripts de l’interpréteur de commandes avec des bashismes ou des zshismes afin de les rendre portables entre tous les interpréteurs POSIX. Vous pouvez le vérifier en utilisant checkbashisms(1).


La commande « echo » doit être utilisée avec les précautions suivantes car son implémentation diffère selon que l’on utilise les commandes internes ou externes de l’interpréteur de commandes :

  • Éviter d’utiliser toutes les options de commandes sauf « -n ».

  • Éviter d’utiliser les séquences d’échappement dans les chaînes de caractères car leur prise en compte varie.

[Note] Note

Bien que l’option « -n » ne soit pas vraiment de la syntaxe POSIX, elle est généralement acceptée.

[Astuce] Astuce

Utilisez la commande « printf » plutôt que la commande « echo » si vous avez besoin d’intégrer des séquences d’échappement dans la chaîne de sortie.

Des paramètres spéciaux de l’interpréteur de commandes sont souvent utilisés dans les scripts de l’interpréteur de commandes.


Basic parameter expansions to remember are as follows.


Ici, les deux points « : » dans tous ces opérateurs sont en fait optionnels.

  • avec « : » = opérateur de test pour existe et différent de null

  • sans « : » = opérateur de test pour existe uniquement


Chaque commande retourne un état de sortie qui peut être utilisé pour des expressions conditionnelles.

  • Succès : 0 (« Vrai »)

  • Erreur : différent de 0 (« Faux »)

[Note] Note

« 0 » dans le contexte conditionnel de l’interpréteur signifie « Vrai » alors que « 0 » dans le contexte conditionnel de C signifie « Faux ».

[Note] Note

« [ » est l’équivalent de la commande test, qui évalue, comme expression conditionnelle, les paramètres jusqu’à « ] ».

Basic conditional idioms to remember are the following.

  • « <commande> && <si_succès_lancer_aussi_cette_commande> || true »

  • « <commande> || <en_cas_de_non_succès_lancer_aussi_cette_commande> || true »

  • Un morceau de script sur plusieurs lignes comme le suivant :

if [ <expression_conditionnelle> ]; then
 <si_succès_lancer_cette_commande>
else
 <si_pas_de_succes_lancer_cette_commande>
fi

Ici, le « || true » était nécessaire pour s’assurer que ce script de l’interpréteur ne se termine pas accidentellement à cette ligne lorsque l’interpréteur est appelé avec l’indicateur « -e ».



Les opérateurs de comparaison arithmétique entière dans les expressions conditionnelles sont « -eq », « -ne », « -lt », « -le », « -gt » et « -ge ».

En gros, l’interpréteur de commandes traite un script de la manière suivante :

  • l’interpréteur de commandes lit une ligne :

  • l’interpréteur de commandes regroupe une partie de la ligne sous forme d’un élément (« token » si elle se trouve entre "…" ou '…' :

  • l’interpréteur de commandes découpe les autres parties de la ligne en éléments comme suit :

    • Espaces : <espace> <tabulation> <saut-de-ligne>

    • Métacaractères : < > | ; & ( )

  • l’interpréteur de commandes vérifie les mots réservés pour chacun des éléments et ajuste son comportement s’il ne se trouve pas entre "…" ou '…'.

    • mot réservé : if then elif else fi for in while unless do done case esac

  • L’interpréteur de commandes étend les alias s’ils ne se trouvent pas entre "…" ou '…'.

  • l’interpréteur de commandes étend les tilde s’ils ne se trouvent pas entre "…" ou '…'.

    • « ~ » → répertoire personnel de l’utilisateur actuel

    • « ~<utilisateur> » → répertoire personnel de l’<utilisateur>

  • l’interpréteur de commandes étend les paramètres en leur valeur s’ils ne sont pas entre '…'.

    • paramètre : « $PARAMETRE » ou « ${PARAMETRE} »

  • l’interpréteur de commandes étend la substitution de commande si elle n’est pas entre '…'.

    • « $( commande ) » → sortie de la « commande »

    • « ` commande ` » → sortie de la « commande »

  • l’interpréteur de commandes étend les motifs génériques du chemin aux fichiers correspondants s’ils ne sont pas entre  "…" ou '…'.

    • * → n’importe quel caractère

    • ? → un caractère

    • […] → un caractère quelconque parmi «  »

  • l’interpréteur de commandes recherche la commande dans ce qui suit et l’exécute.

    • définition de fonction

    • commande interne (« builtin »)

    • fichier exécutable dans « $PATH »

  • l’interpréteur de commandes passe à la ligne suivante et recommence ce traitement depuis le début de la séquence.

Des guillemets simples dans des guillemets doubles n’ont pas d’effet.

Exécuter « set -x » dans le script de l’interpréteur ou l’appel du script avec l’option « -x » fait imprimer par l’interpréteur de commandes toutes les commandes exécutées. C’est assez pratique pour le débogage.

Voici un exemple simple qui crée une image ISO avec des données RS02 fournies par dvdisaster(1) :

#!/bin/sh -e
# gmkrs02 : Copyright (C) 2007 Osamu Aoki <osamu@debian.org>, Public Domain
#set -x
error_exit()
{
  echo "$1" >&2
  exit 1
}
# Initialiser les variables
DATA_ISO="$HOME/Desktop/iso-$$.img"
LABEL=$(date +%Y%m%d-%H%M%S-%Z)
if [ $# != 0 ] && [ -d "$1" ]; then
  DATA_SRC="$1"
else
  # Sélectionner le répertoire pour la création de l’image ISO à partir d'un dossier du bureau
  DATA_SRC=$(zenity --file-selection --directory  \
    --title="Sélectionner la racine de l’arborescence de répertoires pour créer l’image ISO") \
    || error_exit "Quitter lors de la sélection du répertoire"
fi
# Vérifier la taille de l’archive
xterm -T "Check size $DATA_SRC" -e du -s $DATA_SRC/*
SIZE=$(($(du -s $DATA_SRC | awk '{print $1}')/1024))
if [ $SIZE -le 520 ] ; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="La taille des données convient à pour une sauvegarde sur CD :\\n $SIZE Mo"
elif [ $SIZE -le 3500 ]; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="La taille des données convient à pour une sauvegarde sur DVD :\\n $SIZE Mo"
else
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="La taille des données est trop grande pour une sauvegarde : $SIZE Mo"
  error_exit "Taille de données trop importante pour la sauvegarde :\\n $SIZE Mo"
fi
# on n'est certain d'avoir une option -e fonctionnelle uniquement avec xterm
# Créer une image ISO brute
rm -f "$DATA_ISO" || true
xterm -T "genisoimage $DATA_ISO" \
  -e genisoimage -r -J -V "$LABEL" -o "$DATA_ISO" "$DATA_SRC"
# Créer la redondance RS02 supplémentaire
xterm -T "dvdisaster $DATA_ISO" -e  dvdisaster -i "$DATA_ISO" -mRS02 -c
zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
  --text="Données ISO/RS02 ($SIZE Mo) \\n créées sur : $DATA_ISO"
# EOF

Vous désirerez peut-être créer un lanceur sur le bureau avec une commande définie comme « /usr/local/bin/gmkrs02 %d ».

Make est un utilitaire destiné à la maintenance d’un groupe de programmes. Lors de l’exécution de make(1), make lit le fichier de règles, « Makefile » et met à jour une cible si elle dépend de fichiers qui ont été modifiés depuis que la cible a été modifiée pour la dernière fois ou si la cible n’existe pas. L’exécution de ces mises à jour peut être faite simultanément.

La syntaxe du fichier de règles est la suivante :

cible: [ prérequis... ]
 [TAB]  commande1
 [TAB]  -commande2 # ignorer les erreurs
 [TAB]  @commande3 # supprimer l’écho

Ici, « [TAB] » est un code de tabulation. Chaque ligne est interprétée par l’interpréteur de commandes après que make ait effectué la substitution des variables. Utilisez « \ » à la fin d’une ligne pour poursuivre le script. Utilisez « $$ » pour entrer un « $ » pour les valeurs des variables d’environnement d’un script de l’interpréteur de commandes.

On peut écrire des règles implicites pour la cible et les prérequis, par exemple, de la manière suivante :

%.o: %.c header.h

Ici, la cible contient le caractère « % » (exactement 1 caractère). Le caractère « % » peut correspondre à n’importe quelle sous-chaîne non vide des noms de fichiers de la cible actuelle. De même pour les prérequis, utilisez « % » pour afficher la manière dont leur nom est en relation avec le nom de la cible actuelle.



Exécutez « make -p -f/dev/null » afin de voir les règles automatiques internes.

Vous pouvez définir un environnement propre pour compiler des programmes écrits dans le langage de programmation C par ce qui suit :

# apt-get install glibc-doc manpages-dev libc6-dev gcc build-essential

Le paquet libc6-dev, c’est-à-dire la bibliothèque GNU C, fournit la bibliothèque C standard qui est une collection de fichiers d’en-têtes et de routines de bibliothèque utilisée par le langage de programmation C.

Consultez les références pour C comme suit; :

  • « info libc » (références des fonctions de la bibliothèque C)

  • gcc(1) et « info gcc »

  • chaque_nom_de_fonction_de la_bibliothèque_C(3)

  • Kernighan & Ritchie, « Le langage de programmation C », 2ème édition (Prentice Hall)

Le débogage est une partie de l’activité de programmations. Savoir comment déboguer des programmes fera de vous un bon utilisateur de Debian qui pourra produire des rapports de bogues documentés.

Le debogueur primaire sous Debian est gdb(1), il vous permet d’inspecter un programme alors qu’il tourne.

Installons gdb et les programmes associés par ce qui suit :

# apt-get install gdb gdb-doc build-essential devscripts

Un bon didacticiel de gdb est proposé par « info gdb «» ou peut être trouvé ailleurs sur le web. Voici un exemple simple d’utilisation de gdb(1) sur un « program » compilé avec l’option « -g » qui produit les informations de débogage.

$ gdb program
(gdb) b 1                # définit un point d'arrêt à la ligne 1
(gdb) run args           # lancer les programmes avec des paramètres 
(gdb) next               # ligne suivante
...
(gdb) step               # avancer d'un pas
...
(gdb) p parm             # afficher parm
...
(gdb) p parm=12          # définir sa valeur à 12
...
(gdb) quit
[Astuce] Astuce

De nombreuses commandes de gdb(1) possèdent une abréviation. L’expansion à l’aide de la touche de tabulation fonctionne comme avec l’interpréteur de commandes.

Comme, par défaut, sous Debian, tous les paquets binaires sont « strippés » (dépouillés de leurs symboles de débogage), la plupart des symboles de débogage sont supprimés des paquets normaux. Pour pouvoir déboguer des paquets Debian à l’aide de gdb(1), les paquets *-dbg correspondants doivent être installés (par exemple libc6-dbg dans le cas de libc6).

Si un paquet à déboguer ne possède pas de paquet *-dbg correspondant, vous devrez l’installer après l’avoir reconstruit comme suit :

$ mkdir /chemin/nouveau ; cd /chemin/nouveau
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo apt-get install fakeroot devscripts build-essential
$ sudo apt-get build-dep nom_paquet_source
$ apt-get source nom_paquet
$ cd nom_paquet*

Corriger les bogues si nécessaire.

Modifier la version du paquet pour ne pas entrer en collision avec les versions officielles de Debian, par exemple, en ajoutant « +debug1 » pour la compilation d’une version de paquet existante, ou « ~pre1 » pour la compilation d’une version de paquet qui n’est pas encore diffusée de la manière suivante :

$ dch -i

Compiler et installer les paquets avec les symboles de débogage comme suit :

$ export DEB_BUILD_OPTIONS=nostrip,noopt
$ debuild
$ cd ..
$ sudo debi nom_paquet*.changes

Vous devrez vérifier les scripts de construction du paquet et vous assurer que les options « CFLAGS=-g -Wall » sont positionnées pour la compilation des binaires.

Flex est un générateur d’analyse lexicale rapide compatible avec Lex.

On trouve un didacticiel de flex(1) dans « info flex ».

Vous devez fournir vos propres « main() » et « yywrap() ». Sinon votre programme flex devrait ressembler à ce qui suit pour se compiler sans bibliothèque (cela parce que « yywrap » est une macro et que « %option main » active de manière implicite « %option noyywrap ».

%option main
%%
.|\n    ECHO ;
%%

Sinon, vous pouvez compiler avec l’option de l’éditeur de liens « -lfl » à la fin de la ligne de commandes de cc(1) (comme AT&T-Lex avec « -ll »). L’option « %option » n’est pas nécessaire dans ce cas.

Un certain nombre de paquets fournissent un analyseur LR à lecture anticipée (« lookahead ») compatible avec Yacc ou un générateur d’analyseur LALR sous Debian.


On trouve un didacticiel de bison(1) dans « info bison ».

Vous devez fournir vos propre « main() » et « yyerror() ». « main() » appelle « yyparse() » qui appelle « yylex() », habituellement créé avec Flex.

%%

%%

Autoconf est un outil destiné à produire des scripts en shell qui configurent automatiquement un code source de logiciel pour l’adapter à de nombreux types de systèmes « UNIX-like » en utilisant l’ensemble du système de construction GNU.

autoconf(1) produit le script de configuration « configure ». « configure » crée automatiquement un « Makefile » en utilisant le patron « Makefile.in ».

Bien que tous les scripts en AWK puissent être réécrits automatiquement en Perl en utilisant a2p(1), il est plus facile de convertir manuellement les scripts AWK constitués d’une seule ligne en scripts Perl d’une seule ligne.

Regardons le bout de script AWK suivant :

awk '($2=="1957") { print $3 }' |

Il est équivalent à l’une quelconque des lignes suivantes :

perl -ne '@f=split; if ($f[1] eq "1957") { print "$f[2]\n"}' |
perl -ne 'if ((@f=split)[1] eq "1957") { print "$f[2]\n"}' |
perl -ne '@f=split; print $f[2] if ( $f[1]==1957 )' |
perl -lane 'print $F[2] if $F[1] eq "1957"' |
perl -lane 'print$F[2]if$F[1]eq+1957' |

La dernière est une devinette. Elle tire parti des fonctionnalités suivantes de Perl :

  • L’espace est optionnel.

  • Il existe une conversion automatique des nombres en chaîne de caractères.

Consultez perlrun(1) pour les options de la ligne de commandes. Pour des scripts en Perl plus fous, il peut être intéressant de consulter Perl Golf.

Des pages web dynamiques et interactives simples peuvent être faites de la manière suivante :

  • Les requêtes sont présentées au navigateur de l’utilisateur en utilisant des formulaires HTML.

  • Remplir et cliquer sur les entrées de formulaires envoie une des chaînes d’URL suivantes avec des paramètres codés depuis le navigateur vers le serveur web.

    • « http://www.foo.dom/cgi-bin/programme.pl?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3 »

    • « http://www.foo.dom/cgi-bin/programme.py?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3 »

    • « http://www.foo.dom/programme.php?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3 »

  • « %nn » dans l’URL est remplacé par le caractère dont la valeur hexadécimale est nn.

  • La variable d’environnement est définie à : « QUERY_STRING="VAR1=VAL1 VAR2=VAL2 VAR3=VAL3" ».

  • Le programme CGI (l’un quelconque des « programme.* ») sur le serveur web s’exécute lui-même avec la variable d’environnement « $QUERY_STRING ».

  • La sortie standard (stdout) du programme CGI est envoyée au navigateur web et présentée sous forme d’une page web dynamique interactive.

Pour des raisons de sécurité, il est préférable de ne pas réaliser soi-même de nouvelles bidouilles pour analyser les paramètres CGI. Il existe des modules bien établis pour cela, en Perl et Python. PHP est fourni avec ces fonctionnalités. Lorsqu’il est nécessaire d’enregistrer des données du client, on utilise des cookies HTTP. Lorsqu’un traitement de données est nécessaire côté client, on utilise fréquemment Javascript.

Pour davantage d’informations, consultez Common Gateway Interface, The Apache Software Foundation et JavaScript.

Rechercher « CGI tutorial » sur Google en entrant l’URL encodée http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial directement dans la barre d’adresse du navigateur est une bonne méthode pour voir un script CGI en action sur le serveur Google.

Il existe des programmes pour convertir les codes sources.


Si vous désirez créer un paquet Debian, lisez ce qui suit :

Il existe des paquets tels que dh-make, dh-make-perl, etc., qui facilitent la réalisation des paquets.