Skip to content

Ignorer des fichiers, de ack à ag

Ag (the silver searcher), comme ack permettent de chercher des motifs de texte dans du code source. Une sorte de grep spécialisé pour du code source.

Les deux outils sont très probablement disponibles dans votre distribution préférée.
Sous Debian et dérivées :

apt install ack-grep # pour ack
apt install silversearcher-ag # pour ag

ag est plus rapide qu’ack pour trouver des motifs. Un comparatif de performance écrit par le développeur d’ag, qui est donc juge et partie, le montre. Quelques tests rapides m’ont aussi montré un gain de temps.

ack utilise un fichier .ackrc pour ignorer des chemins ou fichiers. ag aussi, mais le format est un peu différent (équivalent à .hgignore et .gitignore qu’il utilise aussi) car il ne fait que de l’exclusion. La modification est triviale pour un castor junior :

$ cat .ackrc
--ignore-dir=riri
--ignore-dir=fifi/
--ignore-dir=loulou

devient
$ cat .ignore
riri
fifi/
loulou

À partir de la version 0.33.0, ag utilise le fichier .ignore et .agignore devient déprécié. Dans la dernière version testée, le fichier .agignore est toujours lu s’il est la racine de ${HOME}, mais non pris en compte s’il est dans le répertoire dans lequel la recherche est faite.

Testé avec les versions suivantes :

ack version 2.12 et 2.18
ag version 0.19.2 et 2.1.0

Publicités

Coder une variante de FizzBuzz basée sur le jeu 6 qui prend

FizzBuzz est un jeu où l’on compte : lorsqu’un entier est un multiple de 3, on dit « Fizz », multiple de 5 « Buzz », multiple de 3 et 5 « FizzBuzz », dans les autres cas, on dit simplement l’entier. Il est aussi utilisé comme test pour évaluer si quelqu’un maîtrise les bases de la programmation. Une variante, un peu plus compliquée, mais du même type pourrait être réalisée avec le jeu 6 qui prend.

C’est un jeu de cartes dont le but est d’avoir le moins de têtes de bœufs. Les cartes sont numérotées de 1 à 104 (inclus).

Les règles d’estimation de la valeur (en tête de bœufs) des cartes sont relativement similaires aux règles du FizzBuzz :

  • si le nombre est un multiple de 5, la carte vaut 2 têtes de bœufs
  • si le nombre est un multiple de 10, la carte vaut 3 têtes de bœufs
  • si le chiffre des dizaines et des unités sont identiques, la carte vaut 5 têtes de bœufs
  • la carte 55 vaut 7 têtes de bœufs
  • les autres cartes valent 1 tête de bœufs chacune

TL; DR Si on a vraiment du temps à perdre, on finit avec un code de ce genre :

CARTES = 104

def tetes(numero):
    _score = 1
    for diviseur, points in ((10, 1), (5, 1), (11, 4)):
        if _est_multiple(numero, diviseur):
            _score += points
    if numero == 55:
        _score += 1
    return _score

def _est_multiple(numero, diviseur):
    return not numero % diviseur

if __name__ == "__main__": 
    for numero in range(1, CARTES + 1):
        print(numero, "★" * tetes(numero))

Le début de la sortie du terminal :

1 ★
2 ★
3 ★
4 ★
5 ★★
6 ★
7 ★
8 ★
9 ★
10 ★★★
11 ★★★★★
12 ★

Première version

Le code le plus rapide à écrire pour passer les tests devrait ressembler à :

def tetes(numero):
    if numero == 55:
        return 7
    if not numero % 10:
        return 3
    elif not numero % 5:
        return 2
    elif numero >= 10 and str(numero)[0] == str(numero)[1]:
        return 5
    else:
        return 1

if __name__ == "__main__": 
    for numero in range(1, 12):
        print(numero, "*" * tetes(numero))

Améliorations

La règle « le chiffre des dizaines et des unités sont identiques » est équivalent à être un multiple de 11 :

#if numero >= 10 and str(numero)[0] == str(numero)[1]:
if not numero % 11:

Autant factoriser le test de divisibilité :

def tetes(numero):
    _score = 1
    if _est_multiple(numero, 10):
        _score += 1
    if _est_multiple(numero, 5):
        _score += 1
    if _est_multiple(numero, 11):
        _score += 4
    if numero == 55:
        _score += 1
    return _score

def _est_multiple(numero, diviseur):
    return not numero % diviseur

On peut alors remplacer la succession de if par une boucle, et hop, on obtient un code final (qui est au début de l’article).

Tests

Pour valider le programme, quelques assertions de bon aloi sont à ajouter. Ces assertions auront été obtenues au fur et à mesure si on fait du TDD.

import sixquiprend

assert 1 == sixquiprend.tetes(1)
assert 1 == sixquiprend.tetes(2)
assert 2 == sixquiprend.tetes(5)
assert 3 == sixquiprend.tetes(10)
assert 5 == sixquiprend.tetes(11)
assert 5 == sixquiprend.tetes(22)
assert 7 == sixquiprend.tetes(55)

Comportement d’éditeurs de texte lors d’une modification concurrente

Comment se comportent les éditeurs de texte lorsqu’un fichier est ouvert dans l’éditeur et qu’il a été modifié en même temps? C’est la question que vous ne vous êtes probablement jamais posée et à laquelle cet article répond.

TL;DR : plus ou moins bien mais l’écrasement sauvage est évité.

Dans un terminal

Le comportement est le même dans les trois éditeurs testés (nano, vim et emacs-nox). L’éditeur signale le problème lors de l’enregistrement du fichier. Il est possible de choisir alors entre l’état du fichier sur le disque ou celui qui vient d’être modifié.
Par contre, on ne s’en rend compte uniquement lorsque l’on désire enregistrer. Il est toujours possible d’annuler, d’enregistrer le fichier modifié sous un autre nom puis faire la comparaison entre les deux.

Nano

C-o

Vim

:w

Emacs-nox

C-x C-s

Emacs propose un choix supplémentaire : r annule les modifications en cours (pour « reverted »). C-h affiche une explication des différents choix et leurs implications.

En mode fenêtré

Le comportement est similaire dans les éditeurs testés (Gvim, Emacs, Gedit et Kate).
Ils détectent la modification automatiquement et propose de charger le fichier mis à jour. En cas de refus, une nouvelle alerte survient lors de l’enregistrement.

Gvim

Détection de la modification :

Alerte à l’enregistrement :

Emacs

Détection de la modification :

Alerte à l’enregistrement :

Gedit

Gedit est l’éditeur de base fourni avec GNOME.

Détection de la modification :

Alerte à l’enregistrement :

Kate

Kate est l’éditeur fourni avec KDE.

Kate propose une fonctionnalité que les autres n’ont pas : lorsque la modification du fichier est détectée, Kate propose de visualiser la différence entre les deux fichiers (bouton « View difference »). L’affichage obtenu est un diff entre la version sur le disque et la version en mémoire. On pourrait imaginer qu’il ouvrirait le résultat dans un nouvel onglet mais il semble qu’il utilise l’éditeur sélectionné par la variable d’environnement $EDITOR.

Détection de la modification :

Alerte à l’enregistrement :

Méthode et versions utilisées pour les tests (et captures d’écran)

Le comportement n’est pas identique entre les éditeurs lorsque la modification du fichier est faite par la commande
echo 'ajout' >> fichier.txt
Par exemple, la modification en parrallèle est bien détectée par Emacs mais pas par Gedit (la détection à l’enregistrement fonctionne correctement).

Pour avoir un comportement homogène, la méthode utilisée à chaque fois a été d’ouvrir le fichier nommé fichier.txt dans deux éditeurs, le modifier et l’enregistrer dans l’un des éditeurs et regarder comment réagit l’autre.

Nano :
2.8.6

Vim :
VIM – Vi IMproved 8.0 (2016 Sep 12, compiled Jul 22 2017 06:10:49)

Gvim :
8.0.707, version de GNOME 3.22.2

Emacs :
25.2.2, avec le thème deeper-blue

Gedit :
3.22.0, version de GNOME 3.22.2

Kate :
16.08.3, avec KDE 5.28.0 et Qt 5.7.1

L’ensemble des captures d’écran ont été faites sous GNOME.

Meld et Mercurial

Meld est un équivalent graphique de `diff` : il permet de faire des différences entre des fichiers de manière plus visuelle. Mercurial peut utiliser `meld` plutôt que l’outil par défaut lorsqu’il faut afficher des différences entre les fichiers du dépôt local et les fichier du dépôt distant.

Mise en œuvre

Il suffit d’activer l’extension Extdiff fournie avec Mercurial. Cela se fait en ajoutant les lignes suivantes au fichier ~/.hgrc (ou au fichier .hg/hgrc si on souhaite limiter la modification à un répertoire versionné) :

[extensions]
hgext.extdiff =

[extdiff]
cmd.meld =

Différences sur un fichier

Supposons cette modification d’un dépôt :

$ hg diff
diff -r 1915567d21b2 jours.py
--- a/jours.py	Sat Jul 08 15:37:19 2017 +0200
+++ b/jours.py	Sat Jul 08 15:37:59 2017 +0200
@@ -1,12 +1,10 @@
 """Module des jours"""
 
-# Trop simple, faire une fabrique abstraite ?
-
 JOURS = ("lundi",
     "mardi",
     "mercredi",
-    "jedi",
+    "jeudi",
     "vendredi",
     "samedi",
-    "dimenche"
+    "dimanche"
     )

En utilisant la commande `hg meld`, une fenêtre de `meld` s’ouvre, montrant l’état dans le dépôt à gauche et l’état du fichier sur le disque à droite :

Cliquer sur les flèches permet de copier le contenu d’un côté à l’autre automatiquement. Il est aussi possible de modifier le contenu directement.

Différences sur plusieurs fichiers

Avec les modifications suivantes par rapport au dernier commit :

$ hg stat
M annees.py
A siecles.py
R heures.py

Lorsque plusieurs fichiers sont modifiés, `hg meld` affiche un premier panneau permettant de voir les fichiers modifiés (en bleu), ajoutés (en vert), absents (grisé et rayé). En double-cliquant sur un des fichiers, un nouvel onglet s’ouvre, affichant la vue détaillée des différences entre des deux fichiers.

Visualisation de conflits

Si `meld` est disponible, Mercurial s’en servira aussi pour la résolution des conflits.
La partie à gauche est l’état du dépôt local, la partie à droite l’état du dépôt distant et la partie centrale l’ancêtre commun au deux fichiers.

Comme dans le cas précédent, il est possible de modifier les fichiers dans `meld` pour résoudre les conflits.

Versions

Cet article a été fait avec meld, version 3.16.4 et mercurial version 4.0. Il a aussi été validé avec meld, version 1.8.4 et mercurial version 2.8.2.

L’image d’en-tête est une superposition des deux logos (Meld et Mercurial), réalisée avec The Gimp.

La meilleure distribution universelle n’existe pas

J’ai publié un article dans Linux Pratique n° 95 (mai-juin 2016) intitulé « La meilleure distribution universelle n’existe pas ». Il est disponible publiquement à http://connect.ed-diamond.com/Linux-Pratique/LP-095/La-meilleure-distribution-universelle-n-existe-pas, sous licence Creative Commons BY-NC-ND.

Les éditions Diamond proposent 3 modes de cession des droits pour les articles dont une qui permet la republication sous cette licence, 6 mois après la publication papier (cession de droits type B).

Dans ce cas où l’article disparaîtrait du site Diamond, je le reproduis ci-dessous :

Pourquoi est-il impossible d’avoir une réponse définitive à la question récurrente de la meilleure distribution Linux ? Si la question suscite de vifs débats, c’est plus souvent dû à une relation passionnelle à l’une d’entre elles qu’à un raisonnement rationnel…

Bien souvent, un nouvel arrivant dans l’écosystème Linux demandera quelle est la meilleure distribution dans le but de l’installer. La réponse est pourtant plus compliquée qu’un simple nom donné rapidement.

Une distribution est un ensemble de logiciels cohérents fournis par une organisation, à but lucratif ou non. Elle peut aussi être représentée par un ensemble de caractéristiques qui la définit. Par exemple, le niveau de fiabilité, la diversité de paquets, la réactivité, l’âge des logiciels empaquetés, etc. Cette liste n’est évidemment pas exhaustive.

Pour connaître la meilleure distribution, il est nécessaire de comparer les distributions. Une fois les distributions triées entre elles, la première du classement pourra être définie comme la meilleure.

1. Comment comparer les distributions ?

Pour comparer les distributions entre elles, il faut définir une relation d’ordre entre elles. Il existe deux types de relation d’ordre.

1.1 Comparaison ordre produit

Supposons deux distributions A et B avec deux caractéristiques : x et y pour A et x’ et y’ pour B.

Définition

L’ordre produit se définit tel que (x, y) ≤ (x’, y’) si et seulement si x ≤ x’ et y ≤ y’.

Comparer deux distributions est donc comparer toutes les caractéristiques (x avec x’ et y avec y’ dans la définition précédente) des deux distributions entre elles. Si une distribution possède toutes ses caractéristiques supérieures (resp. inférieures) à une autre, alors cette distribution est supérieure (resp. inférieure) à l’autre.

Par exemple, on peut considérer que si l’on compare deux versions de la même distribution, la plus récente sera meilleure, car l’ensemble des caractéristiques est équivalent [1] sauf celle de l’âge des paquets. Des paquets plus récents étant préférables, la distribution plus récente est supérieure à la plus ancienne. Assez logiquement, la version 9.04 (Jaunty Jackalope) d’Ubuntu aura des paquets plus vieux qu’une 15.10 (The Wily Werewolf).

De même, on peut trouver des distributions abandonnées ou restées à l’état d’ébauche inférieures à celles qui sont vivantes actuellement en utilisant un ordre produit.

Cependant, ce mode de comparaison a une portée limitée, car c’est une relation d’ordre partiel (il existe des cas non comparables).

Si chaque distribution a quelques caractéristiques supérieures à l’autre, alors elles ne sont pas comparables. Par exemple, supposons qu’il existe deux distributions A et B que l’on note sur deux caractéristiques x et y :

A B
x 3 4
y 5 2

A est préférable sur la caractéristique y et B est préférable sur la caractéristique x. Il n’est donc pas possible de déterminer quelle est la meilleure distribution entre A et B avec un ordre produit.

D’un point de vue pratique, avoir les toutes dernières versions des logiciels augmente les risques d’instabilité, car la fiabilité vient avec les tests et l’expérience, caractéristiques plus faciles à réunir si l’ensemble logiciel reste stable.

Avec le tableau précédent, cela donnerait :

A B
Logiciels récents 3 4
Stabilité 5 2

La distribution A est plus stable alors que B fournit des logiciels plus récents.

Pour contourner cette limite, il est possible de faire une comparaison lexicographique qui offre une relation d’ordre total, c’est-à-dire que l’ensemble des distributions est ordonnable.

1.2 Comparaison lexicographique

Dans un ordre lexicographique, il faut ordonner les caractéristiques par priorité. L’ordre peut être défini en comparant x avec x’, puis y avec y’ si nécessaire.

Définition

L’ordre lexicographique est défini tel que (x, y) ≤ (x’, y’) si et seulement si [x < x’ ou [x = x’ et y ≤ y’]].

Avec le premier tableau, si l’ordre de préférence des caractéristiques est x puis y, alors la meilleure distribution est B. Si l’ordre des préférences est y puis x, alors la meilleure est A. Une fois qu’un ordre entre les caractéristiques est défini, il est possible de trier toutes les distributions et d’obtenir notre champion.

Il est tout à fait possible pour une personne ou une organisation d’ordonner les caractéristiques qui lui importent et de s’en servir pour faire un choix éclairé. Par exemple, pour un serveur, la distribution A serait privilégiée alors qu’un utilisateur voulant les dernières nouveautés aura tendance à se tourner vers la distribution B.

Comme les besoins sont différents pour chaque personne, l’ordre des caractéristiques est lui aussi différent. Cela implique que l’ordre de préférence des distributions le sera aussi. Il n’est donc pas possible d’obtenir un choix universel avec l’ordre lexicographique (alors qu’avec l’ordre produit, on obtient un choix universel sur une relation d’ordre partiel).

Tenter de donner une note globale en se basant sur chaque caractéristique ne change pas le problème. Le choix des caractéristiques à prendre en compte et le poids relatif de chaque caractéristique reviennent au même problème qu’un ordre lexicographique : l’ordre est total, mais la solution n’est pas universelle.

2. Que comparer ?

La difficulté de choix entre les caractéristiques peut sembler plus théorique que pratique. Pourtant, ce n’est pas le cas.

2.1 Caractéristiques incompatibles

Certaines caractéristiques sont incompatibles entre elles.

Le cas de la stabilité contre le fait d’avoir des logiciels récents a été vu dans la partie précédente. Cependant, ce n’est pas le seul exemple possible :

– Avoir une portabilité maximale des logiciels et une quantité maximale de logiciels puisque certains logiciels ne sont pas prévus pour fonctionner sur certaines architectures. Souvent, ce sont les architectures rares ou anciennes qui sont négligées. Debian fournit une version pour dix architectures de processeur, mais certains paquets ne sont pas disponibles sur certaines architectures. Par le passé, certaines architectures (processeurs SPARC, Alpha, Motorola 68k, etc.) ont été abandonnées, car le matériel ou le manque de volontaires pour la maintenance a poussé à arrêter de fournir une version pour eux.

– Avoir une maintenance longue des logiciels et des paquets très à jour. Pour pouvoir maintenir longtemps des paquets, il est nécessaire de limiter les versions des logiciels pour limiter la quantité de versions à maintenir. La maintenance de RHEL (Red Hat Entreprise Linux) est payante, mais dure jusqu’à 10 ans. Il est même possible de l’étendre avec un contrat supplémentaire. Debian fournit une maintenance de deux ans environ pour sa version stable et le projet Debian LTS a pour but d’augmenter la durée de la maintenance à un minimum de 5 ans. Par contre, les paquets de RHEL et Debian stable changent très peu au fil du temps (pour des mises à jour de sécurité uniquement). Au contraire, Archlinux dispose de paquets récents, mais il n’y a pas de maintenance dans le temps, uniquement des mises à jour continues. Fedora, une version communautaire de Red Hat, tente régulièrement l’inclusion de technologies récentes à titre de tests ; la branche Rawhide dispose de paquets très récents. Certains essais seront ensuite intégrés à la version RHEL.

– La facilité d’utilisation est obtenue en faisant des choix raisonnables pour l’utilisateur. C’est contraire à une priorité axée sur la facilité de personnalisation puisque la première étape sera de demander à l’utilisateur de faire des choix. Par exemple, Ubuntu choisit pour l’utilisateur son environnement de bureau (GNOME) et certains paquets visant à aider l’utilisateur sont installés par défaut (comme command-not-found [2]). Au contraire Gentoo nécessite des choix de configuration avancés (comme les flags de compilation des paquets). De même, LFS (Linux From Scratch) est une distribution permettant à l’utilisateur de configurer et construire l’ensemble de son système à la main. Son objectif est plus didactique qu’une réelle utilisation au quotidien. Il est donc logique que la facilité de prise en main soit négligée au profit du contrôle fin par l’utilisateur.

– Une distribution avec un but très spécifique et ayant un paramétrage convenant au plus grand nombre. Par exemple, BackTrack dispose d’outils préinstallés pour les audits de sécurité, tests de pénétration, etc. Il est toujours possible de l’utiliser pour un usage générique. De manière comparable, Tails est conçue pour garantir la confidentialité et l’anonymat de son utilisateur et est utilisable à partir d’un CD ou d’une clef USB. Par défaut, aucune donnée ne sera enregistrée et les données transmises sur Internet passeront par Tor pour cacher l’origine de l’émetteur de la requête. La priorité classique des distributions est de privilégier la vitesse d’échange plutôt que l’anonymat, ce qui est le but même de Tails. De même, l’idée de ne rien enregistrer est incongrue pour une distribution classique.

Au-delà des programmes, des caractéristiques comme le fait de fonctionner de manière ouverte s’opposent à la possibilité d’avoir une communication lisse et contrôlée puisque les débats seront publics. Les distributions communautaires auront plutôt des listes de diffusions ouvertes, rendant les débats et désaccords visibles publiquement sur la plupart des sujets. Les distributions commerciales gardent certains débats privés (en particulier concernant la stratégie, le marketing et la communication), ce qui est logique puisque ce sont des éléments clefs de la réussite d’une entreprise. La transparence s’oppose à la protection de données sensibles.

2.2 Caractéristiques non comparables

Certaines caractéristiques sont d’ordre qualitatif et non quantitatif. On peut préférer une qualité à une autre pour les avantages qu’elle induit, mais la caractéristique en elle-même ne peut pas être classée.

– Distribution source et distribution binaire : une distribution source fournit uniquement le code source et l’utilisateur doit compiler les paquets. Dans une distribution binaire, les paquets sont fournit compilés. Une distribution source sera naturellement plus orientée contrôle par l’utilisateur que facilité d’usage. Les distributions binaires seront plus faciles d’usage puisque les utilisateurs n’auront pas ni à choisir la configuration des compilations, ni attendre la compilation des logiciels. Les paquets restent compilables par l’utilisateur s’il le souhaite. Gentoo est probablement la distribution source la plus connue. Debian, Red Hat, SUSE, Archlinux, etc. sont des distributions binaires.

– Distribution rolling releaseet sortie régulière : les distributions rolling release (mise à jour en continu, sans version fixe) auront plus facilement des logiciels récents que des distributions avec des sorties régulières qui auront des décalages dans le temps. Plus des sorties sont espacées et plus les logiciels vont vieillir. Archlinux est probablement la distribution rolling release la plus connue. Ubuntu a des sorties fixes tous les 6 mois. Fedora fait approximativement de même. Debian publie une version stable approximativement tous les 2 ans. Les distributions à sorties régulières peuvent avoir des versions qui ressemblent à une rolling release pour mettre au point leur prochaine version. Par exemple, Fedora possède une branche nommée Rawhide, Debian utilise unstable et testing.

Conclusion

Selon les priorités de la distribution, leurs caractéristiques seront différentes. Certaines caractéristiques seront plus travaillées que d’autres. C’est ce qui fait la diversité entre les distributions. Cette diversité les rend imparfaitement substituables entre elles. La situation ressemble à une concurrence monopolistique : les distributions sont proches, mais toutes légèrement différentes [3].

Pour un utilisateur débutant, au-delà des caractéristiques de la distribution et son adaptation à son besoin, la facilité à trouver des utilisateurs ayant la même distribution que lui devrait aussi être un critère de choix, car cela lui permettra d’être aidé plus efficacement.

Notes

[1] Les nostalgiques des temps anciens considéreront probablement que c’est une hypothèse à démontrer.

[2] command-not-found est un paquet qui permet d’afficher des suggestions de paquets à installer lorsque la commande saisie dans un terminal par l’utilisateur n’est pas trouvée sur le système.

[3] L’analyse de la concurrence monopolistique a été initiée par E.H Chamberlin en 1933. L’analyse étant basée sur le comportement des entreprises, elle ne s’applique qu’imparfaitement aux distributions Linux. Par contre, la situation de différentiation reste pertinente.