Un peu de libre dans ce monde propriétaire

On y parle de libre, de configuration et d'autres trucs hermétiques à la plupart des personnes

Aller au contenu | Aller au menu | Aller à la recherche

17avr. 2011

Oripa & Ubuntu

Oripa est un outil développé en Java qui permet de concevoir des canevas de plis (ou Crease Pattern) de modèles en origami et de les plier virtuellement.

Pour le faire fonctionner, il est nécessaire d'avoir Java d'installé et plus particulièrement le Java Runtime Environment (JRE). Pour ce faire, un petit tour sur le terminal et on installe le paquet nécessaire et ses dépendances :

  1. sudo apt-get install sun-java6-jre

Puis on lance Oripa avec la commande suivante :

  1. java -jar oripaXXX.jar
  2. # où XXX est le numéro de version. 034 à la date d'écriture de ce billet

Hélas, lors du premier lancement, j'ai été confronté à l'erreur suivante :

  1. Exception in thread "main" java.lang.NoClassDefFoundError: javax/vecmath/Tuple2d
  2. at oripa.ORIPA.main(ORIPA.java:54)
  3. Caused by: java.lang.ClassNotFoundException: javax.vecmath.Tuple2d
  4. at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
  5. at java.security.AccessController.doPrivileged(Native Method)
  6. at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
  7. at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
  8. at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
  9. at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
  10. ... 1 more

Je n'avais pas vu sur la page d'Oripa que le programme nécessite l'API Java 3D. On va donc installer le paquet nécessaire :

  1. sudo apt-get install libjava3d-java

Et on relance.

Malheureusement, j'ai toujours la même erreur. Après quelques recherches, je suis tombé sur cette page qui m'a fourni des pistes pour résoudre mon problème. J'ai juste créé un lien symbolique à l'endroit nécessaire :

  1. sudo ln -s /usr/share/java/vecmath-1.5.2.jar /usr/lib/jvm/java-6-sun/jre/lib/ext/vecmath-1.5.2.jar

Et voila, ça fonctionne :)

Édition du 29 juin 2011 : La méthode proposée en faisant un lien symbolique n'est pas des plus propres. Il y a une méthode plus adaptée pour utiliser la bibliothèque vecmath. Il suffit de d'ajouter un répertoire dans la liste des répertoires lus par Java pour récupérer les bibliothèques. Donc, au lieu de lancer Oripa de la manière suivante :

  1. java -jar oripaXXX.jar
  2. # où XXX est le numéro de version. 034 à la date d'écriture de ce billet

on le lance en lui ajoutant un paramètre avec le chemin des bibliothèques :

  1. java -Djava.ext.dirs=/usr/share/java/ -jar oripaXXX.jar
  2. # où XXX est le numéro de version. 034 à la date d'écriture de ce billet

C'est plus propre, non?

11avr. 2011

Opérations sur la hiérarchie : suite

Les précédents articles[1][2] sur le stockage et la manipulation des hiérarchies dans MySQL sont loin d'être complets. J'y apporte donc un complément avec 2 nouvelles requêtes de manipulations.
On conserve la même table et les même données que dans les articles précédents afin de rester cohérent.

Déplacer un élément

Dans l'article Un peu d'ordre dans la hiérarchie, nous avons déjà abordé le déplacement d'un élément mais juste vers un autre élément de la hiérarchie. Cette fois ci, nous allons aborder le déplacement d'un élément à la racine, c'est à dire qu'il n'aura plus de parent. Nous allons donc déplacer l'élément B vers la racine. Pour cela, il suffit de lancer ces quelques requêtes :

  1. /* Verrouillage en écriture de la table*/
  2. LOCK TABLE tree WRITE;
  3.  
  4. /* Récupération des informations de l'élément à déplacer */
  5. SELECT @nodeLeft := lft
  6. , @nodeRight := rgt
  7. , @nodeRange := rgt - lft + 1
  8. FROM tree
  9. WHERE name = 'B';
  10.  
  11. /* Récupération des informations de déplacement de l'élément */
  12. SELECT @offsetRange := MAX(rgt) - @nodeRight
  13. FROM tree;
  14.  
  15. /* Exclusion temporaire de l'élément à déplacer */
  16. UPDATE tree
  17. SET lft = -lft
  18. , rgt = -rgt
  19. WHERE lft >= @nodeLeft
  20. AND rgt <= @nodeRight;
  21.  
  22. /* Mise à jour des bornes droites des éléments concernés */
  23. UPDATE tree
  24. SET rgt = rgt - @nodeRange
  25. WHERE rgt >= @nodeRight;
  26.  
  27. /* Mise à jour des bornes gauches des éléments concernés */
  28. UPDATE tree
  29. SET lft = lft - @nodeRange
  30. WHERE lft >= @nodeRight;
  31.  
  32. /* Repositionnement de l'élément à déplacer */
  33. UPDATE tree
  34. SET lft = -lft + @offsetRange
  35. , rgt = -rgt + @offsetRange
  36. WHERE rgt < 0;
  37.  
  38. /* Suppression des verrous */
  39. UNLOCK TABLES;

Ce qui nous donne ceci :

+-----------+------+------+----+
| NAME      | LFT  | RGT  | ID |
+-----------+------+------+----+
| A         |    1 |    8 |  1 |
| ----C     |    2 |    7 |  5 |
| --------F |    3 |    4 |  6 |
| --------G |    5 |    6 |  7 |
| B         |    9 |   14 |  2 |
| ----D     |   10 |   11 |  3 |
| ----E     |   12 |   13 |  4 |
+-----------+------+------+----+
7 rows in set (0.00 sec)

Récupérer le niveau d'un élément

Utilisons la requête suivante :

  1. SELECT COUNT(p.name) - 1 AS level
  2. FROM tree p
  3. , tree n
  4. WHERE n.lft BETWEEN p.lft AND p.rgt
  5. AND n.name = 'G'
  6. GROUP BY n.id

Ce qui nous donne ceci :

+-------+
| level |
+-------+
|     2 |
+-------+
1 row in set (0.00 sec)

25fév. 2011

Relancer un service avec cron

Dis comme ça, ça paraît simple.
Effectivement c'est très simple. Par exemple, pour relancer le service d'apache, il suffit d'ajouter la ligne suivante au cron :

  1. */1 * * * * /usr/bin/pgrep apache || /etc/init.d/apache2 start

Pour l'explication de la syntaxe, je vous laisse le soin de lire l'introduction à cron et le tutoriel de relance automatique des services.

Mais si ça avait été aussi simple dans mon cas, je n'aurais pas écrit ce message. J'ai été confronté à un problème qui m'a demandé pas mal de temps à résoudre.

Le problème était le suivant. J'avais un script de relance d'un service un peu plus complexe que celui décrit un peu plus haut. Le script fonctionnait parfaitement lancé manuellement avec l'utilisateur root. Par contre, il ne fonctionnait pas du tout quand il était lancé par cron (toujours en root).

Après de nombreux tests différents, j'ai identifié le problème. La variable PATH utilisée par cron ne contenait pas le chemin d'accès au programme appelé et ce même si l'utilisateur était le même.
Je ne sais pas si c'est dû à la configuration de Debian et d'Ubuntu [1], mais la configuration de cron [2] n'est pas prise en compte au moment de l'exécution des commandes. J'ai du ajouter manuellement le chemin d'accès dans le script.

Finalement, la solution était vraiment toute simple mais identifier le problème a été la partie la plus complexe.

Notes

[1] j'ai fait le test sur les 2 plate-formes

[2] dans le fichier /etc/crontab

19oct. 2010

Copier des fichiers par un canal sécurisé

La semaine passée j'ai dû copier des fichiers entre plusieurs machines. J'ai fait comme d'habitude, j'ai utilisé scp de la manière suivante :

  1. scp host:fileWithoutSpaces .

Jusqu'au moment où je suis tombé sur un fichier comportant des espaces. Cette syntaxe n'était plus du tout valide car scp considère les espaces comme des séparateurs de fichiers. Dans ce cas, il faut utiliser la syntaxe suivante :

  1. scp host:"file\ with\ spaces" .

Il ne faut oublier ni les " ni les \.

En recherchant cette syntaxe, j'ai trouvé quelques petites choses intéressantes à faire avec scp. Alors en bonus :

  1. # Copier plusieurs fichiers en une seule fois :
  2. scp host:"file1 file2 file3" .
  3.  
  4. # Copier un répertoire complet :
  5. scp -r host:folder .
  6.  
  7. # Copier plusieurs fichiers vers une machine distante
  8. scp file1 file2 file3 host:.
  9.  
  10. # Copier un fichier avec espaces vers une machine distante
  11. scp file\ with\ spaces host:.
  12. # ou
  13. scp "file with spaces" host:.

15oct. 2010

Opérations sur la hiérarchie

Suite au précédent article sur les hiérarchies avec MySQL, je vais ajouter quelques requêtes pratiques. On utilisera la même table et les mêmes données.

Récupérer le chemin d'un élément

Cet exemple [1] est décrit dans le document qui a initié ce billet ainsi que le précédent. Je le replace ici car il va servir de base aux requêtes suivantes.
Recherchons le chemin pour l'élément E, pour cela, utilisons la requête suivante :

  1. /* Récupération du chemin d'un élément */
  2. SELECT p.name AS NAME
  3. FROM tree AS n
  4. , tree AS p
  5. WHERE n.lft BETWEEN p.lft AND p.rgt
  6. AND n.name = 'E'

Ce qui nous donne ceci :

+------+
| NAME |
+------+
| A    |
| B    |
| E    |
+------+
3 rows in set (0.00 sec)

Nous remarquons que le chemin comporte également l'élément sélectionné.

Récupérer le chemin d'un élément sans l'élément

Utilisons la requête suivante :

  1. /* Récupération du chemin d'un élément sans l'élément en question */
  2. SELECT p.name AS NAME
  3. FROM tree AS n
  4. , tree AS p
  5. WHERE n.lft BETWEEN p.lft AND p.rgt
  6. AND n.name = 'E'
  7. AND n.id <> p.id

Ce qui nous donne ceci :

+------+
| NAME |
+------+
| A    |
| B    |
+------+
2 rows in set (0.00 sec)

Récupérer le parent immédiat d'un élément

Utilisons la requête suivante :

  1. /* Récupération du parent immédiat d'un élément */
  2. SELECT p.name AS NAME
  3. FROM tree AS n
  4. , tree AS p
  5. WHERE n.lft BETWEEN p.lft AND p.rgt
  6. AND n.name = 'E'
  7. AND n.id <> p.id
  8. ORDER BY p.lft DESC
  9. LIMIT 1

Ce qui nous donne ceci :

+------+
| NAME |
+------+
| B    |
+------+

Rien de bien compliqué, mais ça dépanne quand on en a besoin.

Notes

[1] Pour le trouver dans la documentation originale, il faut chercher le titre Retrieving a Single Path. Il y a 2 occurrences dans le document, celle qui nous intéresse est la seconde.

30sept. 2010

Un peu d'ordre dans la hiérarchie

Il y a plusieurs méthodes pour stocker une hiérarchie de données dans une base de données MySQL [1], elles sont toutes les deux décrites dans ce document [2]. Il est assez complet, mais il me manque cependant quelques requêtes pour la manipulation des données selon la méthode de l'arbre. On va donc mettre en place ces requêtes.

Première étape : mise en place de l'environnement

Pour cela, nous avons besoin d'une table et de quelques données.

  1. /* Création de la table */
  2. CREATE TABLE tree
  3. ( id int AUTO_INCREMENT
  4. , name varchar(3)
  5. , lft int
  6. , rgt int
  7. , PRIMARY KEY (id));
  8.  
  9. /* Insertion des données */
  10. INSERT INTO tree (name,lft,rgt)
  11. VALUES ('A',1,14)
  12. , ('B',2,7)
  13. , ('D',3,4)
  14. , ('E',5,6)
  15. , ('C',8,13)
  16. , ('F',9,10)
  17. , ('G',11,12);

Maintenant, nous pouvons vérifier la structure de notre arbre avec la requête suivante [3]:

  1. /* Affichage de l'arbre de manière visuelle */
  2. SELECT concat(repeat('----',(count(p.name)-1)),n.name) AS NAME
  3. , n.lft AS LFT
  4. , n.rgt AS RGT
  5. , n.id AS ID
  6. FROM tree AS n
  7. , tree AS p
  8. WHERE n.lft BETWEEN p.lft
  9. AND p.rgt
  10. GROUP BY n.id
  11. ORDER BY n.lft;

Ce qui nous donne ceci :

+-----------+------+------+----+
| NAME      | LFT  | RGT  | ID |
+-----------+------+------+----+
| A         |    1 |   14 |  1 |
| ----B     |    2 |    7 |  2 |
| --------D |    3 |    4 |  3 |
| --------E |    5 |    6 |  4 |
| ----C     |    8 |   13 |  5 |
| --------F |    9 |   10 |  6 |
| --------G |   11 |   12 |  7 |
+-----------+------+------+----+
7 rows in set (0.00 sec)

Deuxième étape : ajouter un élément

J'ai décidé de n'ajouter un élément que sous un autre élément. Donc le nouvel élément se trouvera être le dernier enfant de son parent. Pour faire cela, il suffit de lancer ces quelques requêtes :

  1. /* Verrouillage en écriture de la table*/
  2. LOCK TABLE tree WRITE;
  3.  
  4. /* Récupération des informations de l'élément parent */
  5. SELECT @parentRight := rgt
  6. FROM tree
  7. WHERE name = 'B';
  8.  
  9. /* Mise à jour des bornes droites des éléments concernés */
  10. UPDATE tree
  11. SET rgt = rgt + 2
  12. WHERE rgt >= @parentRight;
  13.  
  14. /* Mise à jour des bornes gauches des éléments concernés */
  15. UPDATE tree
  16. SET lft = lft + 2
  17. WHERE lft >= @parentRight;
  18.  
  19. /* Insertion du nouvel élément */
  20. INSERT INTO tree (name,lft,rgt)
  21. VALUES ('ZZZ',@parentRight,@parentRight + 1);
  22.  
  23. /* Suppression des verrous */
  24. UNLOCK TABLES;

Ce qui nous donne ceci :

+-------------+------+------+----+
| NAME        | LFT  | RGT  | ID |
+-------------+------+------+----+
| A           |    1 |   16 |  1 |
| ----B       |    2 |    9 |  2 |
| --------D   |    3 |    4 |  3 |
| --------E   |    5 |    6 |  4 |
| --------ZZZ |    7 |    8 | 11 |
| ----C       |   10 |   15 |  5 |
| --------F   |   11 |   12 |  6 |
| --------G   |   13 |   14 |  7 |
+-------------+------+------+----+
8 rows in set (0.00 sec)

Troisième étape : déplacer un élément

J'ai décidé de ne gérer que les déplacements vers un parent qui ne fait pas parti des enfants de l'élément. Pour prendre le cas précédent, je peux déplacer B vers C mais pas vers ZZZ. Pour faire cela, il suffit de lancer ces quelques requêtes :

  1. /* Verrouillage en écriture de la table*/
  2. LOCK TABLE tree WRITE;
  3.  
  4. /* Récupération des informations de l'élément à déplacer */
  5. SELECT @nodeLeft := lft
  6. , @nodeRight := rgt
  7. , @nodeRange := rgt - lft + 1
  8. FROM tree
  9. WHERE name = 'ZZZ';
  10.  
  11. /* Récupération des informations du nouvel élément parent */
  12. SELECT @newParentLeft := lft
  13. , @newParentRight := rgt
  14. FROM tree
  15. WHERE name = 'C';
  16.  
  17. /* Calcul de la valeur de déplacement de l'élément à déplacer */
  18. SELECT @offsetRange := @newParentRight - @nodeRight - 1 + IF( sign( @newParentRight - @nodeRight ) < 1 , @nodeRange , 0 );
  19.  
  20. /* Calcul de la borne gauche de déplacement des autres éléments */
  21. SELECT @offsetLeft := IF( sign( @offsetRange ) = 1 , @nodeRight , @newParentRight );
  22.  
  23. /* Calcul de la borne droite de déplacement des autres éléments */
  24. SELECT @offsetRight := IF( sign( @offsetRange ) = 1 , @newParentRight - 1 , @nodeLeft - 1 );
  25.  
  26. /* Calcul de la valeur de déplacement des autres éléments */
  27. SELECT @signedNodeRange := sign( @offsetRange ) * @nodeRange;
  28.  
  29. /* Exclusion temporaire de l'élément à déplacer */
  30. UPDATE tree
  31. SET lft = -lft
  32. , rgt = -rgt
  33. WHERE lft >= @nodeLeft
  34. AND rgt <= @nodeRight;
  35.  
  36. /* Mise à jour des bornes droites des éléments concernés */
  37. UPDATE tree
  38. SET rgt = rgt - @signedNodeRange
  39. WHERE rgt BETWEEN @offsetLeft AND @offsetRight;
  40.  
  41. /* Mise à jour des bornes gauches des éléments concernés */
  42. UPDATE tree
  43. SET lft = lft - @signedNodeRange
  44. WHERE lft BETWEEN @offsetLeft AND @offsetRight;
  45.  
  46. /* Repositionnement de l'élément à déplacer */
  47. UPDATE tree
  48. SET lft = -lft + @offsetRange
  49. , rgt = -rgt + @offsetRange
  50. WHERE rgt < 0;
  51.  
  52. /* Suppression des verrous */
  53. UNLOCK TABLES;

Ce qui nous donne ceci :

+-------------+------+------+----+
| NAME        | LFT  | RGT  | ID |
+-------------+------+------+----+
| A           |    1 |   16 |  1 |
| ----B       |    2 |    7 |  2 |
| --------D   |    3 |    4 |  3 |
| --------E   |    5 |    6 |  4 |
| ----C       |    8 |   15 |  5 |
| --------F   |    9 |   10 |  6 |
| --------G   |   11 |   12 |  7 |
| --------ZZZ |   13 |   14 | 11 |
+-------------+------+------+----+
8 rows in set (0.00 sec)

Quatrième étape : supprimer un élément

Pour faire cela, il suffit de lancer ces quelques requêtes :

  1. /* Verrouillage en écriture de la table*/
  2. LOCK TABLE tree WRITE;
  3.  
  4. /* Récupération des informations de l'élément à supprimer */
  5. SELECT @nodeRight := rgt
  6. , @nodeLeft := lft
  7. , @range := rgt - lft + 1
  8. FROM tree
  9. WHERE name = 'ZZZ';
  10.  
  11. /* Suppression de l'élément */
  12. DELETE FROM tree
  13. WHERE lft >= @nodeLeft
  14. AND rgt <= @nodeRight;
  15.  
  16. /* Mise à jour des bornes droites des éléments concernés */
  17. UPDATE tree
  18. SET rgt = rgt - @range
  19. WHERE rgt >= @nodeRight;
  20.  
  21. /* Mise à jour des bornes gauches des éléments concernés */
  22. UPDATE tree
  23. SET lft = lft - @range
  24. WHERE lft >= @nodeRight;
  25.  
  26. /* Suppression des verrous */
  27. UNLOCK TABLES;

Ce qui nous donne ceci :

+-----------+------+------+----+
| NAME      | LFT  | RGT  | ID |
+-----------+------+------+----+
| A         |    1 |   14 |  1 |
| ----B     |    2 |    7 |  2 |
| --------D |    3 |    4 |  3 |
| --------E |    5 |    6 |  4 |
| ----C     |    8 |   13 |  5 |
| --------F |    9 |   10 |  6 |
| --------G |   11 |   12 |  7 |
+-----------+------+------+----+
7 rows in set (0.00 sec)

Et voila. J'ai fait les tests de déplacement et de suppression d'arbres et ça fonctionne correctement. Ne me croyez pas sur parole, essayez. Par contre, vous avez remarqué que je n'ai pas fait de test de validation des données. Si vous voulez allez plus loin, je vous laisse le soin de les faire.

Notes

[1] Pour les autres bases de données aussi, mais ce n'est pas le propos du jour.

[2] La lecture de ce document est un pré-requis pour la compréhension de ce qui va suivre.

[3] Nous utiliserons cette requête pour valider l'ensemble des résultats ultérieurs.

25août 2010

Renommer des fichiers

Aujourd'hui, nous allons renommer des fichiers avec les outils en ligne de commande.

Renommages simples avec l'outil mv

Pour renommer fichier1 en fichier2

  1. mv fichier1 fichier2

ou

  1. mv fichier{1,2}

Pour renommer fichier1 en fichier1.bak

  1. mv fichier1{,.bak}

mv est pratique pour renommer un fichier à la fois. Si vous en avez plus que 2, il vaut mieux passer à un autre outil.

Renommages moins simples avec l'outil rename

L'outil rename fonctionne grâce aux expressions rationnelles [1][2][3][4] mais malheureusement, nous ne sommes pas dans un monde parfait car l'outil a quelques particularité dans l'interprétation qu'il fait de celles-ci.

Pour renommer fichier1 et fichier2 en fichier1.bak et fichier2.bak

  1. rename 's/$/.bak/' fichier1 fichier2

ou

  1. rename 's/$/.bak/' fichier{1,2}

Pour renommer fichier 1 et fichier 2 en fichier1.bak et fichier2.bak

  1. rename 's/ ([1,2])$/$1.bak/' fichier\ {1,2}

Pour renommer premier gros fichier en premier.Gros.Fichier

  1. rename 's/ (.)/.\u$1/g' premier\ gros\ fichier

Pour renommer exemple d'un gros fichier en exemple.D.Un.Gros.Fichier

  1. rename 's/[ '"'"'](.)/.\u$1/g' exemple\ d\'un\ gros\ fichier

Notez dans cet exemple la manière dont il faut échapper l'apostrophe (').

Pour renommer fichier1 et fichier2 en FICHIER1 et FICHIER2

  1. rename 's/(.)/\u$1/g' fichier{1,2}

ou

  1. rename 'y/a-z/A-Z/' fichier{1,2}

Pour renommer FICHIER1 et FICHIER2 en fichier1 et fichier2

  1. rename 's/(.)/\l$1/g' FICHIER{1,2}

ou

  1. rename 'y/A-Z/a-z/' FICHIER{1,2}

Bon amusement pour renommer vos fichiers maintenant que vous avez quelques outils.

21août 2010

Envoyer un courriel

Récemment, j'ai eu besoin d'envoyer un courriel automatiquement car à une heure à laquelle je dormais (oui, ça m'arrive parfois :) ). J'ai alors cherché à envoyer un message automatiquement.

Je me suis donc tourné vers les outils disponibles sur la distribution de mon serveur. Et j'y ai trouvé l'utilitaire mail qui répond exactement au besoin que j'avais. On peut utiliser ce logiciel en mode interactif en l'appelant simplement depuis la console ou en mode autonome. C'est cette deuxième méthode qui nous intéresse.

Pour commencer, il est nécessaire de créer un fichier contenant le message à envoyer, appelons ce fichier message.A.Envoyer. Ensuite il faut envoyer le contenu du message à mail pour qu'il l'envoie à l'adresse sélectionnée :

  1. cat message.A.Envoyer | mail -s "Sujet du message" address@to.com

Voila, c'est aussi simple que ça.

Si on regarde de plus près le message reçu, on s'aperçoit que dans l'entête l'origine est le courriel de notre serveur. Si c'est le courriel que l'on utilise, il n'y a pas de problèmes, sinon on risque de ne jamais recevoir de réponse. Toutefois, avec mail, il n'y a pas vraiment de problèmes car on peut modifier les valeurs de l'entête :

  1. cat message.A.Envoyer | mail -s "Sujet du message" -a 'From: "Mon nom" <address@from.com>' address@to.com

Toutes les autres valeurs de l'entête sont modifiables de la même manière.

On peut également envoyer le message en copie et en copie cachée :

  1. cat message.A.Envoyer | mail -s "Sujet du message" -a 'From: "Mon nom" <address@from.com>' -b address.blind.carbon.copy@to.com -c address.carbon.copy@to.com address@to.com

Avec ces quelques options, on couvre déjà beaucoup de besoins. Il ne reste plus qu'à appeler notre script depuis cron pour envoyer le message aux dates et heures désirées.

06juil. 2010

Vérifier la somme de contrôle d'un fichier

Quand je fais des téléchargements de gros fichiers, comme des distributions Linux, j'aime savoir si l'intégrité de mon fichier est préservée. Pour le savoir, j'ai recours aux sommes de contrôle.

Qu'est ce qu'une somme de contrôle [1] ou empreinte?

C'est le résultat, de longueur fixe, de la transformation d'un fichier par une fonction de hachage [2]. Selon la théorie des codes, à chaque empreinte correspond un seul et unique fichier. Mais ce n'est que de la théorie, car la recherche faisant des progrès, ce n'est plus tout à fait vrai [3].

De ce fait, il est facile ensuite de vérifier si le fichier est bien ce qu'il prétend être en comparant son empreinte réelle avec son empreinte théorique.

Il existe différentes fonctions de hachages, mais les plus courantes (en tout cas sur GNU/Linux) sont MD5, SHA1 et SHA256. On préfèrera l'utilisation de l'algorithme SHA pour des raisons évidentes de sécurité (voir annexes).

Vérification de l'intégrité d'un fichier

D'abord, téléchargeons un fichier ainsi que son empreinte. On peux prendre l'exemple de l'ISO de la version 13 de Fedora et de son empreinte SHA256. Dans ce fichier, ce qui nous intéresse, c'est la ligne suivante

47ccc37db256387b70857f53a6067e8d50e692c9aa85e45e63e5190c5d1e0942 *Fedora-13-i686-Live.iso
Première méthode : la mauvaise

On peux calculer l'empreinte de notre fichier de la manière suivante :

sha256sum Fedora-13-i686-Live.iso

et on obtiendra l'empreinte à comparer (manuellement) avec l'empreinte originale. C'est très chiant rébarbatif et source d'erreurs. On préfèrera donc l'une des 2 méthodes suivantes.

Deuxième méthode : en 1 seule commande

En fait, dans la première méthode, on a utilisé le programme sha1sum pour générer une empreinte, alors qu'il y a moyen de lui fournir un fichier et une empreinte afin qu'il nous dise si le fichier correspond à l'empreinte.
Il y a plusieurs syntaxes différentes, je vous en propose 2 :

echo "47ccc37db256387b70857f53a6067e8d50e692c9aa85e45e63e5190c5d1e0942 *Fedora-13-i686-Live.iso" | sha256sum -c -
sha256sum -c - <<< "47ccc37db256387b70857f53a6067e8d50e692c9aa85e45e63e5190c5d1e0942 *Fedora-13-i686-Live.iso"

Je préfère la seconde syntaxe car il y a moins à écrire et qu'il n'y a qu'un seul programme appelé. Mais les 2 sont équivalentes.

Notons que le paramètre -c indique que la commande va vérifier un fichier et son empreinte, que le paramètre - indique que la commande attend la chaîne de caractères formatée sur son entrée standard.

L'inconvénient de cette méthode, c'est que l'on ne peut traiter qu'un seul fichier à la fois. Pour traiter plusieurs fichiers à la fois, on utilise la troisième méthode.

Troisième méthode : en 1 commande et un fichier

La syntaxe est assez similaire et doit ressembler à ça :

sha256sum -c FICHIER_DE_CONTROLE

où FICHIER_DE_CONTROLE est le nom du fichier contenant 1 chaîne de caractères formatée par ligne.

La chaîne de caractères formatée [4]

Elle est composée de 4 éléments :

  • l'empreinte du fichier à vérifier
  • un espace
  • une étoile ou astérisque (*)
  • le nom du fichier à vérifier