Journal écriture d'un script AWK de transformation de Markdown en HTML

Posté par  . Licence CC By‑SA.
43
20
oct.
2025

Bonjour nal,

Ceci est un retour d'expérience sur l'écriture d'un script AWK de transformation de Markdown en HTML.

Ce script n'a pas vocation à être utilisé. Il y a déjà des outils mûres pour faire la même chose, en mieux.

Ce script est avant tout une expérimentation pour savoir si cela est faisable.

L'expérimentation est plutôt concluante puisqu'en quelques 80 lignes et un après midi de taf, le script couvre l'essentiel de Markdown :

  • en-tête de chapitre #
  • bloc de code et code intégré `
  • images et liens [text](url)
  • styles gras, barré et italique **bold**
  • listes non ordonnées/ordonnées/imbriquées * foo
  • listes de tâches - [ ] make journal
  • tableaux |a|b|
  • citation >
  • ligne horizontale ---

Mais il n'est pas réellement robuste. Si le Markdown est volontairement alambiqué, le résultat n'est pas garanti.

Markdown

Markdown est omniprésent, dans les CMS (comme ce site), les forums (discourse), les chats (matrix), les forges (Gitea), les générateurs de sites statiques, les LLM. Bref, à peu près tout ce qui doit gérer du contenu texte.

Ce qui rend le concept puissant c'est que, par opposition au "markup", la syntaxe n'est pas contraignante. Ainsi, tout texte brut est aussi du Markdown et c'est à l'outil de tenté de faire un habillage.

Alors bien sûr si on écrit n'importe quoi, le résultat ne sera pas très beau, mais au moins on aura pas un "parsing error". On aura le texte brut, mais on ne perd pas de texte.

Étonnamment, Markdown n'est pas supporté nativement par les navigateurs web. Pour pallier ce manque, il existe une foultitude de programmes transformant du Markdown en HTML, dans tous les langages imaginables. Pourquoi pas en AWK.

AWK

Lorsque l'on développe en AWK, il faut avoir quelques principes en tête :

  • AWK traite l'input une ligne après l'autre.
  • Une ligne d'input est présente dans le contexte du programme sous sa forme entière (variable $0) et des champs qui la compose lorsqu'elle est découpé par un séparateur (variables $1 .. $NF).
  • Lorsqu'il traite une ligne, AWK applique le script un bloc après l'autre, dans l'ordre.
  • Chaque bloc de script est composé d'une condition et d'un bloc actions.
  • Si la ligne d'input courante remplit la condition, les actions sont exécutées.
  • Il est possible de mettre fin au script pour la ligne d'input courante et de passer à la ligne d'input suivante avec le mot clé next ; les blocs suivants ne sont pas exécutés.

Et ainsi appliquer les recettes suivantes:

  • organiser le code pour avoir les traitements prioritaires en haut.
  • au besoin, garder un état d'une ligne d'input à la suivante en utilisant des variables. Dans ce cas, il faut bien s'assurer de réinitialiser l'état le cas échéant
  • au besoin, utiliser les tableaux associatifs pour stocker des états plus complexe, type hiérarchie

md2html.awk

Le script est organisé comme ceci:

  • on traite d'abord tout ce qui ne doit pas subir de style, et en particulier les blocks de code ; et on stop le traitement
  • on applique les styles ; et on continue le traitement
  • on applique les liens et images ; et on continue le traitement
  • on applique les en-tête de chapitre ; on stop
  • on applique les traitements lorsqu'on est à l'intérieur d'un tableau ; on stop
    • on utilise ici une variable pour stocker le numéro de ligne dans le tableau
  • on applique les traitements lorsqu'on est à l'intérieur d'une liste ; on stop
    • on utilise ici un tableau associatif pour stocker la hiérarchie de listes imbriquées
  • si l'on est pas rentré dans le cas tableau ou liste, on est donc sorti de ce cas, on réinitialise leur état
  • le cas par défaut est d'être dans un bloc de texte

Travaux antérieurs

J'ai voulu dans un premier temps produire le code "en chambre" afin de ne pas être influencé. Ensuite j'ai regardé ce qui avait déjà été fait. Il y a quelques pépites, surtout celles implémentant une machine à état, qui doivent être bien plus robustes que mon implémentation, mais aussi bien plus complexes.

Conclusion

J'aime beaucoup AWK et je crois savoir pourquoi : il y a une grande quantité de problèmes de traitement de données qui peuvent être résolus de manière locale (en flux), sans nécessité de traiter le document au global. Et cela conduit à des solutions très élégantes à mon goût.


Je ne sais pas si ce type de journal vous intéresse mais moi j'aimerai bien lire plus d'histoires de développeurs.

  • # Moi aussi j'aime bien lire les histoires de développeurs

    Posté par  (site web personnel) . Évalué à 10 (+10/-0).

    Et j'adore ces programmes qui viennent d'une époque où la mémoire était une contrainte forte, et ou le traitement par ligne était nécessaire car les fichiers ne tenaient pas en mémoire :-)

  • # Incroyable...

    Posté par  (site web personnel) . Évalué à 10 (+16/-0).

    J'ai un vieux md2html.awk qui traîne…
    Si ça t'intéresse, je peux le mettre sur un git public. Le code fait 620 lignes et le code pas très bô (côté organisation… c'est option bourrage papier). Il gère une part de markdown, la transformation de schéma ASCII en svg, par exemple :

     ╔══════════════════╗   ╔══════════════════╗ 
     ║  Concept         ║   ║                  ║ 
     ║     opt  1       ◄───►   Test           ║ 
     ║     opt  2       ║   ║                  ║ 
     ║     opt  3       □───►                  ║ 
     ╚══════════════════╝   ╚══════════════════╝ 
    

    Et j'ai, sans doute, rajouté des trucs qui me faisait plaisir à l'époque :)

  • # Moi j'ai réussi à lui faire résoudre un sudoku

    Posté par  . Évalué à 10 (+11/-0). Dernière modification le 20 octobre 2025 à 10:59.

    Ce script est avant tout une expérimentation pour savoir si cela est faisable.

    Idem pour moi, c'était surtout pour apprendre awk :) C'était au milieu des années 2000 il me semble, et j'ai retrouvé le mail avec le code il y a deux ou trois semaines … Faudrait que je le diffuse quelque part si ça vous intéresse.

    J'aime beaucoup AWK et je crois savoir pourquoi : il y a une grande quantité de problèmes de traitement de données qui peuvent être résolus de manière locale (en flux), sans nécessité de traiter le document au global. Et cela conduit à des solutions très élégantes à mon goût.

    Idem. Beaucoup ont le reflexe python quand il faut traiter des documentss mais souvent un petit script awk bien senti permet de se passer de python, de sa lourdeur et surtout de ses uv, poetry et virtualenv ou à la c*n et du tas de libs qu'on traine derriere.

    Je ne sais pas si ce type de journal vous intéresse mais moi j'aimerai bien lire plus d'histoires de développeurs.

    Ah, je devrais peut-être en faire un journal alors :)

    • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

      Posté par  (Mastodon) . Évalué à 10 (+11/-0).

      perl5 est aussi pas mal comme alternative à awk.

      Je n'est pas testé raku.

      Python c'est triste. Si je veux faire un programme lent je le fait en Ruby, au moins il aura du style.

    • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

      Posté par  . Évalué à 5 (+3/-0). Dernière modification le 20 octobre 2025 à 15:20.

      je te rejoins, Python qui était une idée assez sympa au départ est devenu une sorte de Léviathan indigeste du fait de sa propre popularité : comme tout le monde l'utilise, ils font des optimisations cassant les versions précédentes, comme ça "bah les dev qui l'utilisent n'ont qu'à faire des mises à jour de leur code voire tout réécrire hein".

      Ils ont cassé pas mal de choses en passant de la v2 à la v3 (wicd c'était sympa comme logiciel mais il ne fonctionne plus et il a été retiré des dépôts parce que debian ne fournit plus de python v2), et à chaque mise à jour ils modifient parfois en profondeur certains points.

      Il doit y avoir 2 types de dev, ceux qui préfèrent avoir des outils simples et performants, et ceux qui préfèrent les outils qui font "gagner" du temps au début, mais qui sont obligés ensuite de tout réécrire pour suivre les mises à jour de l'outil et de ses 40 dépendances, mais "tant mieux on a gagné 5 ms d'optimisation dans notre mise en prod sur un langage qui est lent et qui n'était prévu que pour réaliser du prototypage de base"

      C'est donc pas mal d'utiliser des outils basés sur un langage qui "évolue" peu, comme awk.

      Pour OP, quelques remarques :

      • la licence utilisée interdit d'apporter des améliorations ou modifications à ton code (cc-by-nc-nd), je voulais le porter pour txt2tags au lieu de markdown, je ne peux pas.

      • ça serait bien d'indiquer comment l'utiliser dans l'entête (awk -f md2htlm.awk fichier.md)

      • j'ai fait quelques tests, ça fonctionne, mais parfois bizarrement. Ce fichier est converti, mais la fin des noms de fichier png ou jpg est tronquée :

      ![](img/photo.jpg) devient <img src="img/photo.jp">)

      https://raw.githubusercontent.com/txt2tags/txt2tags/refs/heads/v3/samples/sample.md

      par contre celui-ci, qui est légèrement différent et ne semble pas trop tordu, produit une infinité de je pense que la syntaxe des tableaux n'est peut être pas légale, ou qu'il y en a trop au même endroit…

      https://raw.githubusercontent.com/txt2tags/txt2tags/refs/heads/v2/samples/sample.md

      Rappel important : vos amis qui se sont retournés contre vous parce que la TV leur a dit de le faire : ils le feront encore.

      • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

        Posté par  . Évalué à 4 (+2/-0).

        la licence utilisée interdit d'apporter des améliorations ou modifications à ton code (cc-by-nc-nd), je voulais le porter pour txt2tags au lieu de markdown, je ne peux pas.

        Je peux adapter, quelle licence me suggères-tu ?

        ça serait bien d'indiquer comment l'utiliser dans l'entête (awk -f md2htlm.awk fichier.md)

        done ; au passage j'ai corrigé la typo sur le nom de fichier (:facepalm:)

        j'ai fait quelques tests, ça fonctionne, mais parfois bizarrement. Ce fichier est converti, mais la fin des noms de fichier png ou jpg est tronquée :

        Tu dois utiliser gawk et tu dois avoir des warnings lors de l'interprétation de l'expression régulière utilisée pour les liens.

        J'ai ajouté une note comme quoi cela ne fonctionne qu'avec mawk ; c'est souvent le awk par défaut … ou pas :)

      • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

        Posté par  . Évalué à 3 (+1/-0).

        194    * The filename must end in PNG, JPG, GIF, or similar.
        195    * No spaces inside the brackets!
        

        Il y a des puces de niveau 2 dans puce de niveau 1. J'imagine que c'est ça qui fait péter car ça part en boucle infini sur des fermetures de liste </ul>. D'ailleurs en remettant ce deux puces en début de ligne, ça passe. J'ai pas trop envie de compliquer le script pour gérer ce cas …

      • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

        Posté par  . Évalué à 7 (+5/-0). Dernière modification le 20 octobre 2025 à 16:32.

        Ils ont cassé pas mal de choses en passant de la v2 à la v3 (wicd c'était sympa comme logiciel mais il ne fonctionne plus et il a été retiré des dépôts parce que debian ne fournit plus de python v2)

        AAAh . Je ne suis pas le seul à qui ce soft manque terriblement :(

        Il doit y avoir 2 types de dev, ceux qui préfèrent avoir des outils simples et performants, et ceux qui préfèrent les outils qui font "gagner" du temps au début, mais qui sont obligés ensuite de tout réécrire pour suivre les mises à jour de l'outil et de ses 40 dépendances, mais "tant mieux on a gagné 5 ms d'optimisation dans notre mise en prod sur un langage qui est lent et qui n'était prévu que pour réaliser du prototypage de base"

        Python perd pas mal en popularité au profit de go à cause de ça (et du fait que pour un pauvre script on a besoin de UV + virtualenv, packaging non uniformisé,- alors que Go c'est juste un exécutable avec outil de build performant, sans compter les libs de vérification de type à ajouter pour palier le typage dynamque par exemple).

        • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

          Posté par  . Évalué à 3 (+3/-1).

          Go c'est surtout des exécutables super gros y compris pour des programmes courts, si j'ai bien testé ?

          • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

            Posté par  . Évalué à 5 (+3/-0).

            Si on joue à qui a la plus grosse (taille), je pense que Python dépasse go haut la main. Tu as le python core + lib standard, tout ce que tu installes via pip et ton code à toi. Pas sûr que Python prenne moins de place pour le coup.

            • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

              Posté par  . Évalué à 2 (+1/-0).

              Gros par environnement virtuel. Si tu groupes plusieurs petits programmes dans un même venv, tu mutualises une partie de la charge en python. Tu n'as pas ce choix (même s'il n'est pas parfait) en Go il me semble ?

              Au final ce qu'il faudrait c'est un langage de haut niveau compilable vers des petits exécutables pour des programmes courts (avec élagage des dépendances par exemple).

              Mais sinon awk c'est très bien aussi en effet pour de la manipulation de texte (et sed, et uniq, et sort, et wc, etc…)

              • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

                Posté par  . Évalué à 8 (+6/-0).

                Au final ce qu'il faudrait c'est un langage de haut niveau compilable vers des petits exécutables pour des programmes courts (avec élagage des dépendances par exemple).

                Ça existe et ça s'appelle Nim.

              • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

                Posté par  . Évalué à 3 (+1/-0).

                Gros par environnement virtuel. Si tu groupes plusieurs petits programmes dans un même venv, tu mutualises une partie de la charge en python. Tu n'as pas ce choix (même s'il n'est pas parfait) en Go il me semble ?

                En go tu crée un utilitaire avec des sous commandes (exemple terraform init, terraform plan, terraform apply). On retrouve ce pattern également avec git, ou d'autres utilitaires python.

                Perso pour faire du CI CD je prefere largement du go que du python (et pas que pour ça d'ailleurs, mais c'est un des trucs qui me gavent en python).

                Au final ce qu'il faudrait c'est un langage de haut niveau compilable vers des petits exécutables pour des programmes courts (avec élagage des dépendances par exemple).

                C'est aussi un truc auquel j'ai déjà rêvé..

                Mais sinon awk c'est très bien aussi en effet pour de la manipulation de texte (et sed, et uniq, et sort, et wc, etc…)

                Yes, ça évite souvent de sortir la massue pour écraser un moustique

      • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

        Posté par  . Évalué à 9 (+7/-0).

        Franchement Python 3 ça remonte et depuis ça casse pas trop, et il y a des temps de dépréciation super longs. Je trouve l'environnement plutôt stable.

        Mais je reconnais que le packaging met du temps à se standardiser. (Tout le monde reconnaît ça.)

        • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

          Posté par  . Évalué à 5 (+3/-0). Dernière modification le 21 octobre 2025 à 19:15.

          Il est trop tard à mon avis …. Python va perdre du terrain sur pas mal de trucs au profit de go qui est bien plus facilement packageable, typé, plus rapide, gère nativement le parallelisme et la concurrence, et n'a pas besoin de cette saleté de virtualenv. En Ci/Cd c'est juste la copie d'un exécutable ou une installation via l'outil de build/packaging go là ou les devs python font souvent du copy dégueulasse vers leur image docker au lieu de packager proprement leur truc (un pip install est bien plus propre qu'un copy de mon point de vue, et rend le truc installable indépendamment du contexte docker ou autre).

          • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

            Posté par  . Évalué à 2 (+1/-1). Dernière modification le 22 octobre 2025 à 00:53.

            Python va perdre du terrain sur pas mal de trucs au profit de go

            Je suis prêt à prendre le pari inverse. Reste à définir l'indicateur.

            Python est présent partout

            • datascience : numpy, panda, SciPy
            • big data : PySpark
            • IA : Scykit learn, pytorch
            • API : fastapi, flask
            • web scientifique : streamlit, gradio, jupyternb
            • application web: django
            • GUI : wxpython, kivy

            Go : faire de serveur gRPC.

            Ça devait permettre de faire de la programmation système mais a manqué sa cible à cause du GC et de la difficulté de binding avec C, donc supplanté dans ce domaine par un Zig ou un Rust.

            Et Python est très accessible aux débutants / non informaticiens grâce à sa syntaxe, son absence de typage et justement son écosystème ; pour un effort de 100, tu vas fournir 20 et l'écosystème 80 alors que en Go, l'écosystème va fournir 40 et tu fournira 60 (pifométrique).

            Donc le packaging, à côté de tout ça, ça reste anecdotique dans le choix, AMHA.

            • [^] # J'ai justement une question de python

              Posté par  (site web personnel) . Évalué à 6 (+4/-0). Dernière modification le 22 octobre 2025 à 05:26.

              Donc le packaging, à côté de tout ça, ça reste anecdotique dans le choix, AMHA.

              Ah ben ça tombe bien ça, parce que j'ai justement une question à ce propos.

              Contexte.

              Je voudrais simplifier la compilation du Frido qui est un gros document LaTeX dont la compilation dépend d'un bon script en python. Je voudrais ne pas utiliser le python du système ni polluer la home de l'utilisateur. Bref : je veux que tout se passe dans le répertoire où l'utilisateur aura cloné les sources.

              Ma question

              Je suis dans le répertoire /home/laurent/foobar. Je fais

              git clone https://github.com/pyenv/pyenv.git ./pyenv
              cd pyenv/bin
              ./pyenv install -v 3.10.12

              Et là, il installe quand même dans ~/.pyenv/versions.

              Comment faire pour installer dans /home/laurent/foobar/pyenv/versions ?

              • [^] # Re: J'ai justement une question de python

                Posté par  . Évalué à 5 (+3/-0).

                Ah ben ça tombe bien ça, parce que j'ai justement une question à ce propos.

                Ta question porte sur l'installation d'un environnement de développement, pas sur le packaging.

                Le "packaging" ça consiste à empaqueter une application : en faire un zip ou alors le mettre sur un registre (pypi, npm, crates.io) avec toutes ses dépendances. Pour obtenir le premier résultat - un gros paquet auto porteur - j'utilise PyInstaller. Injustement méconnu alors qu'il marche très bien.

                Concernant ta question:

                Je ne connais pas pyenv. Sans savoir si il permet de faire ce que tu souhaites, j'aurai envisagé de créer un lien symbolique ~/.pyenv/versions pointant vers ~/foobar/pyenv/versions.

                Mais en suivant le lien que tu donnes, je vois un README bien fourni.

                Il y a une section B. Set up your shell environment for Pyenv. Pour bash, le détail dit :

                echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc

                Je me dis qu'en faisant

                echo 'export PYENV_ROOT="$HOME/foobar/pyenv"' >> ~/.bashrc

                tu devrais sans doute obtenir l'effet escompté.

                PS: merci pour ton travail sur Le Frido, c'est une pépite.

                • [^] # Re: J'ai justement une question de python

                  Posté par  (site web personnel) . Évalué à 4 (+2/-0). Dernière modification le 23 octobre 2025 à 06:33.

                  Au cas où quelqu'un tomberait ici, la solution qui marche est :

                  En bash:

                  cd ~/tmp/foobar/pyenv/bin
                  PYENV_ROOT=/home/moky/tmp/foobar/pyenv ./pyenv install -v 3.10.12

                  (je n'ai pas envie de polluer le ~/.bashrc de l'utilisateur)

            • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

              Posté par  . Évalué à 2 (+0/-0).

              Ça devait permettre de faire de la programmation système mais a manqué sa cible à cause du GC et de la difficulté de binding avec C, donc supplanté dans ce domaine par un Zig ou un Rust.

              Et encore il y a micropython qui est un peu utilisé dans l'embarqué.

              Et par exemple sur tasmota il y a un python like : berry. Ca permet des extensions sans tout rebuild : très pratique.

              Ca remplace un peu lua que certains avait utilisé en embarqué, mais dont la syntaxe est moins folle.

    • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

      Posté par  (site web personnel) . Évalué à 3 (+2/-0). Dernière modification le 21 octobre 2025 à 08:42.

      Beaucoup ont le reflexe python quand il faut traiter des documentss mais souvent un petit script awk bien senti permet de se passer de python, de sa lourdeur et surtout de ses uv, poetry et virtualenv ou à la c*n et du tas de libs qu'on traine derriere.

      Il n'y a vraiment pas besoin d'outils additionnels (uv, virtualenv, bibliothèques externes, etc.) pour réaliser l'équivalent en Python. Si certains vont le faire en Python, AMA, c'est parce que (au choix) :

      • c'est le langage avec lequel ils sont le plus à l'aise
      • le code doit être intégré dans un ensemble plus grand, qui lui, n'est pas en awk
      • awk est inconnu des auteurs (au sens où ils ignorent même son existence)

      Ce serait aussi vrai avec Ruby, Perl, Javascript, etc.

      • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

        Posté par  . Évalué à 6 (+4/-0).

        Il n'y a vraiment pas besoin d'outils additionnels (uv, virtualenv, bibliothèques externes, etc.) pour réaliser l'équivalent en Python.

        Si tu n'utilises pas de lib externe et que tu veux un truc one shot, effectivement c'est faisable. Python est plutôt bon pour faire du prototype rapide. Mais ès que tu commence à avoir des dépendances et que tu veux packager, tu arrives vite à l'enfer.

        • [^] # Re: Moi j'ai réussi à lui faire résoudre un sudoku

          Posté par  (site web personnel) . Évalué à 3 (+2/-0).

          Il y a du travail actuellement pour améliorer la situation : le fichier pyproject.toml contient la plupart des informations nécessaires pour construire une bibliothèque ou gérer les dépendances d'un projet. setuptools est en train de supprimer des fonctionnalités, ce qui peut casser des constructions qui fonctionnait jusque-là, mais le but est d'avoir un meilleur découpage des responsabilités entre les différents outils.

          La doc est meilleure, sans être idéale. À mon avis, un des moyens les plus faciles pour faire un paquet est de regarder comment un paquet simple et bien maintenu fait et de reproduire en lisant la doc correspondante pour comprendre un peu ce qu'on fait.

          Historiquement, un des trucs qui était vraiment pas pratique était l'inclusion de code C. Je ne sais pas la situation a changé sur ce point.

  • # AWK, c’est comme Perl en moins bien

    Posté par  (site web personnel) . Évalué à 10 (+11/-2).

    Cette phrase s’applique aussi au shell.

    Ce n’est pas étonnant, puisque quand Larry Wall, alors administrateur système, a créé Perl, il s’est inspiré de ces outils (sûrement pas mal d’AWK) pour créer un langage polyvalent, mais conservant leurs possibilités.

    Concernant AWK, il y a des options pour simuler son comportement avec Perl :
    -n pour parcourir ligne à ligne les fichiers passés en arguments,
    -e pour exécuter le programme passé en paramètre de la ligne de commande (plutôt qu’un fichier ; avec awk, c’est l’inverse, le premier argument est considéré par défaut comme le programme à exécuter, sauf si un fichier est précisé avec l’argument -f),
    -a pour séparer les lignes en champs selon les espaces (ou un autre séparateur passé en argument avec -F, comme awk)
    (voir man perlfunc pour les options).

    Et dans cette utilisation, ça supporte aussi les blocs BEGIN et END, comme awk.

    En gros, un uniligne en AWK de la forme :

    awk '/expression régulière/ { instructions }' fichier_a_traiter
    

    devient

    perl -ane 'if (/expression régulière/) { instructions }' fichier_a_traiter

    Alors pour un traitement simple, il y a effectivement trois options à passer en plus.
    Seulement si le traitement à effectuer se complexifie, avec la lecture d’autres fichiers dans un format différent à analyser, des recoupements à faire, ça devient moins évident avec AWK, alors que Perl fournit les possibilités d’un langage généraliste.

    man perlrun nous apprend que l’option -n correspond à la construction :

    while (<>) {
        traitement;
    }

    donc dans un programme plus complet (sans -n, mais non plus la nécessité d’utiliser BEGIN ni END), on parcourra un fichier avec une construction comme :

    open my $fich, '<fichier';
    while (<$fich>) {
        traitement;
    }
    close $fich;

    Quelques différences par rapport à AWK :

    • la fin de ligne ne vaut pas fin d’instruction, il faut un point‐virgule pour séparer les instructions ;
    • les variables sont toujours préfixées (par $ pour les variables scalaires) ;
    • la ligne courante est dans la variable $_ (avec l’option -n ou while(<>)), les champs dans $F[0], $F[1] (avec l’option -a)…
    • else if est contracté en elsif.

    Il y a une commande pour traduire un script AWK en Perl, malheureusement un peu tombée en désuétude (elle a longtemps été intégrée à la bibliothèque Perl de base).

    Il y a une bibliothèque de base fournie, et énormément de modules complémentaires dans le CPAN pour interagir avec un annuaire LDAP, faire une interface graphique… C’est là que ça dépasse nettement les possibilités d’AWK.

    Quelques autres choses à savoir :

    • comme en shell, les variables sont interprétées dans les chaînes entre guillemets ", pas dans les chaînes entre quotes ' ;
    • les variables locales se déclarent avec my (si on commence son programme par use strict; les variables doivent être déclarées, ça évite d’avoir des bugs à cause d’une typo, c’est donc fortement conseillé pour tout ce qui est plus gros qu’un uniligne) ;
    • les variables scalaires sont préfixées par $, les tableaux par @, les tableaux associatifs par %, mais un élément d’un tableau @t s’accède avec $t[indice] et un élément d’un tableau associatif %a avec $a{chaîne} ; il faut voir @ et % comme des marques de pluriel (Larry Wall est aussi linguiste…) ;
    • $0 contient la ligne de commande, @ARGV ses arguments ;
    • for (@tableau) permet de traiter tous les éléments d’un tableau (affectés à $_), mais for supporte aussi la syntaxe du langage C ;
    • des éléments entre parenthèses et séparés par des virgules forment une liste, qui peut être utilisée pour initialiser un tableau ou un tableau associatif (un élément sur deux est l’indice et l’autre la valeur) ;
    • le type des variables scalaires est déterminé par le contexte et la conversion faite si possible ; par exemple . l’opérateur de concaténation considère ses arguments comme des chaînes de caractères, + comme des nombres, && comme des booléens ;
    • par défaut vrai correspond à 1 et faux à 0, mais la chaîne vide et la liste vide sont aussi considérées comme des valeurs fausses, les autres comme vraies ;
    • les fonctions se définissent avec sub, les paramètres se récupèrent dans @_.

    Par exemple le code AWK,

    function styles(pat, tag) {
      l = length(pat)
    

    se traduira par

    sub styles {
       my ($pat, $tag) = @_;
       my $l = length $pat;

    Note : comme on le voit, on peut aussi affecter un tableau à une liste, les éventuelles valeurs surnuméraires sont ignorées. Mais si la fonction attend une liste de longueur indéterminée d’arguments de rôle équivalent, on peut aussi les traiter directement avec for (@_) { traitement sur $_ }.

    Je me suis concentré sur les particularités, le reste ressemble plus aux autres langages, notamment au langage C ou au shell.

    Prendre une bonne disposition : beop.free.fr

    • [^] # Re: AWK, c’est comme Perl en moins bien

      Posté par  . Évalué à 9 (+7/-0).

      Merci pour cette longue description qui permettra à tout un chacun chacune d'estimer son appétence envers la syntaxe Perl. Pour ma part 🤢

      Je m'en tiendrait à la maxime

      Ne le fait pas avec Python si tu peux le faire avec Perl
      Ne le fait pas avec Perl si tu peux le faire avec Awk
      Ne le fait pas avec Awk si tu peux le faire avec Sed
      Ne le fait pas avec Sed si tu peux le faire avec Grep

      Et j'ajouterai que AWK faisant partie du standard Posix, cela en fait une dépendance sur laquelle on peut quasiment toujours compter, même dans des environnements restreints. Commet le shell et contrairement à tous les autres langages de scripts.

      / # awk
      BusyBox v1.37.0 (2025-05-26 20:04:45 UTC) multi-call binary.
      
      / # perl
      sh: perl: not found
      • [^] # Perl or not Perl

        Posté par  (site web personnel) . Évalué à 5 (+4/-0).

        Merci pour cette longue description qui permettra à tout un chacun chacune d'estimer son appétence envers la syntaxe Perl. Pour ma part 🤢

        J’ai plus fait le tour des particularités à connaître quand on vient d’un autre langage que des trucs sympas.

        Par exemple, si on veut un tableau d’entiers allant de 1 à 10 : my @t = 1 .. 10;

        Il y a des choix inusuels qui permettent une certaine souplesse.
        Par exemple, si une fonction attend des paramètres fixes et un nombre variable d’autres paramètres, on peut les récupérer avec my ($a, $b, $c, @val) = @_;

        Il y a certaines fonctionnalités bien pratiques, comme la fonction map, qui remplace ses arguments par le résultat du bloc de commandes ou de la chaîne passée en premier paramètre, appliquée à chacun un par un, et retourne cela en liste. C’est plus court et moins lourd à lire qu’un for avec un ajout dans un tableau. Mais on ne commence pas forcément par ça en venant d’un autre langage.
        C’est moins gratifiant de commencer par s’assurer de pouvoir faire ce qu’on savait déjà faire dans d’autres langages, mais ça cause sûrement moins de frictions.

        #!/usr/bin/perl -w
        use strict;
        
        # ax² + bx + c
        sub f {
            my ($a, $b, $c, @val) = @_;
            return map { $a * $_ ** 2 + $b * $_ + $c } @val;
        }
        
        print map "$_\n", f(2, 3, 1, 1 .. 10);

        Je m'en tiendrait à la maxime

        Ne le fait pas avec Python si tu peux le faire avec Perl

        Bonne idée : du coup, il n’y a aucune raison d’utiliser Python ;-) (OK, NumPy).
        En plus, ça évite de devoir se coltiner des virtualenv parce que la bibliothèque truc nécessite la bibliothèque bidule 15, mais ne fonctionne pas avec la 16, parce que la notion de compatibilité ascendante n’a malheureusement pas pénétré l’écosystème Python.

        Ne le fait pas avec Perl si tu peux le faire avec Awk

        Je regrette vraiment qu’AWK ait été cantonné à du parcours de fichier. À mon sens, il aurait mérité de remplacer le shell.

        Ne le fait pas avec Awk si tu peux le faire avec Sed

        Oui, enfin au delà de la substitution, sed devient vite ésotérique (un peu entre les CSS et la configuration de sendmail). Utiliser sed à fond disqualifie pour se plaindre de la lisibilité de Perl.

        Ne le fait pas avec Sed si tu peux le faire avec Grep

        Bon, j’utilise aussi grep pour les cas triviaux.

        J’ai des collègues qui pensent comme ça aussi.
        Le problème, c’est que parfois les besoins évoluent.

        On commence à faire un truc en shell en « pipant » des commandes, et on s’aperçoit que ce serait bien de traiter une autre donnée en parallèle, sauf que ce n’est pas très pratique à faire dans des pipes, et donc on finit par refaire le traitement dans un vrai script avec une logique très différente.

        Ou on commence par écrire un script shell, puis on le complexifie jusqu’au stade où on se dit que ce n’est pas très pratique ou très rapide (n’importe quel langage qui n’appelle pas des programmes externes pour des commandes de base est beaucoup plus rapide que le shell) et qu’on ferait mieux de le refaire en Python.

        Alors que Perl est évolutif. Tu commences par une uniligne pas plus longue que celle avec des pipes (en utilisant les options comme -n et sans déclarer les variables). Puis s’il tu dois passer à un vrai script, tu ajoutes use strict;, tu déclares les variables, tu remplaces les options par leur équivalent explicite, mais tu n’as pas à refaire ton code, juste à le compléter. Et si un script doit devenir plus complexe, pas besoin de changer de langage, tu as déjà un langage bien plus puissant que le shell.

        Et j'ajouterai que AWK faisant partie du standard Posix, cela en fait une dépendance sur laquelle on peut quasiment toujours compter, même dans des environnements restreints.

        Attention, awk, c’est comme l’eau Cristaline, ce ne sont pas tous le même. Et certaines fonctionnalités utiles ne font pas partie de la version de base.

        Commet le shell et contrairement à tous les autres langages de scripts.

        C’est pire pour le shell. Si tu ne sais pas si ton script sera exécuté sur bash ou sur un vieux ksh même pas Posix (la dernière fois que j’ai essayé OpenIndiana, c’était le cas de son /bin/sh, pour ne pas casser la compatibilité de vieux scripts écrits il y avait déjà plusieurs décennies), ça limite beaucoup.

        Et il y a des « bashismes » qui sont vraiment utiles, comme "﹩@" (j’ai remplacé les dollars par leur version mini dans ce paragraphe, parce qu’autrement, Markdown me remplace la suite par du Mathjax ou au mieux par la chaîne {mathjax} quand je tente de le quoter avec \ ; seule une lettre derrière le dollar semble empêcher ce comportement pénible) pour passer les arguments de la fonction courante à une autre fonction ou commande sans connaître leur nombre et sans se prendre un gros bug si certains contiennent des espaces. Les shells moins évolués ne disposent que de ﹩*. Tel quel, il met tous les arguments dans la ligne de commande sans guillemets, et "﹩*" les passe tous comme un seul argument entre guillemets. Dans les deux cas, s’il y en avait qui contenaient des espaces, il n’y a plus moyen de les séparer de manière fiable. Alors que "﹩@" fait comme s’ils étaient passés chacun entre guillemets. C’est moins logique par rapport à la syntaxe, mais c’est le comportement dont on a réellement besoin.

        Si on en vient à devoir installer bash, autant installer Perl.

        Prendre une bonne disposition : beop.free.fr

        • [^] # Re: Perl or not Perl

          Posté par  (site web personnel) . Évalué à 2 (+0/-0).

          Mais on ne commence pas forcément par ça en venant d’un autre langage.

          Ça dépend de quel langage tu viens. :)

          Adhérer à l'April, ça vous tente ?

          • [^] # Re: Perl or not Perl

            Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0).

            C’est sûr que si tu viens de Rust
            …OK, j’exagère, on parlait de langage de script… donc on va dire Ruby par exemple
            Ou, parce-que c’est vendredi c’est la Java

            “It is seldom that liberty of any kind is lost all at once.” ― David Hume

        • [^] # Re: Perl or not Perl

          Posté par  (site web personnel) . Évalué à 3 (+1/-0). Dernière modification le 24 octobre 2025 à 21:18.

          (j’ai remplacé les dollars par leur version mini dans ce paragraphe, parce qu’autrement, Markdown me remplace la suite par du Mathjax ou au mieux par la chaîne {mathjax} quand je tente de le quoter avec \ ; seule une lettre derrière le dollar semble empêcher ce comportement pénible)

          moui, c'est connu le $ doublé qui donne du mathjax ;-) (et difficilement contournable, mieux vaut une bonne connaissance des regexp :p)

          je te laisse essayer de mettre deux signes $ sur la même ligne :p perso, j'y arrive regarde : et hop un $ de plus :D ça te permettra d'écrire correctement $@ et $* \o/ (mais pas dans du code entre backquote `` :/ et — forcément — si quelqu'un a le malheur de te citer, il verra l'étendue des dégâts /o\)

        • [^] # Re: Perl or not Perl

          Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0). Dernière modification le 24 octobre 2025 à 21:47.

          Le point nazigrammar avant de poursuivre :
          🆗 Je note aussi tout en majuscule pour le langage standardisé, et les commandes en respectant leur casse Unix (souvent tout en minuscule du coup), parfois la casse de titre pour évoquer une saveur particulière.
          ⛔️ L’autre standard est tout en majuscules aussi… pas en casse de titre et surtout jamais en minuscules (toute façon c’est un sigle …et il ne viendrait à l’idée d’aucune personne de France et des environs Navarre d’écrire « sncf » ou « edf » par exemple.)

          Et j'ajouterai que AWK faisant partie du standard Posix, cela en fait une dépendance sur laquelle on peut quasiment toujours compter, même dans des environnements restreints.

          Attention, awk, c’est comme l’eau Cristaline, ce ne sont pas tous le même. Et certaines fonctionnalités utiles ne font pas partie de la version de base.

          Je pense que quand on évoque le AWK portable c’est qu’on prend le dénominateur commun des saveurs existantes (Nawk, Gawk, Mawk, et d’autres.)
          Et quand on évoque le AWK POSIX c’est qu’on s’en tient aux fonctionnalités standardisées par la norme (donc pas juste le standard de fait qui rend la fonctionnalité portable mais le cachet officiel)
          Et du coup si on a besoin de fonctionnalités qui ne font pas partie de la version de base, bah on fait une petite fonction maison qui l’implémente et qui est livrée avec le code. Et au bout d’un moment on sait qu’il est temps de changer d’outil ;)

          Comme le shell et contrairement à tous les autres langages de scripts.

          C’est pire pour le shell. Si tu ne sais pas si ton script sera exécuté sur bash ou sur un vieux ksh même pas Posix (la dernière fois que j’ai essayé OpenIndiana, c’était le cas de son /bin/sh, pour ne pas casser la compatibilité de vieux scripts écrits il y avait déjà plusieurs décennies), ça limite beaucoup.

          J’interprète le « Comme le shell » comme, dans la même logique, d’écrire en « shell POSIX » et donc avec des fonctionnalités garanties même s’il y en a moins et qu’elles peuvent être plus limitées que le supershell…
          Donc soit ton OpenIndiana n’est pas vraiment un compatible Unix, soit son /bin/sh tout vieux Korn qu’il soit est compatible POSIX (en fait Ksh l’est —mais on va dire à 99.90% pour laisser une place à une éventuelle subtilité inconnue— tant que tu n’utilise pas de fonctionnalité qui lui soit propre)
          Pour poursuivre, "﹩*" et "﹩@" ne sont pas des bashismes mais bien des trucs exigés par POSIX. Et non, les deux sont utiles (même si toi tu n’as « réellement » besoin que d’un cas) :

          C’est moins logique par rapport à la syntaxe, mais c’est le comportement dont on a réellement besoin.

          Revenons un peu en arrière et au sujet.

          Je regrette vraiment qu’AWK ait été cantonné à du parcours de fichier. À mon sens, il aurait mérité de remplacer le shell.

          Justement non… Le besoin n’était pas d’avoir un autre shell mais juste un outil à utiliser avec les autres shells (Thomson, Bourne, C, Korn, qu’importe)
          Ça ne fait que du traitement de fichiers et ça le fait bien, sans devoir s’emmerder avec tous les autres aspects que doit gérer le shell (copier et déplacer des nœuds du système de fichiers, passer des arguments entre des commandes, créer et supprimer des entrées dans le système de fichiers, plein d’autres choses en se préoccupant chaque fois des autorisations d’accès et des différents flux connectés par chaque usager du système.)

          Oui, enfin au delà de la substitution, sed devient vite ésotérique (un peu entre les CSS et la configuration de sendmail).

          CSS n’a rien d’ésotérique :)
          Et sed est très bien quand on l’utilise pour ce pour quoi c’est prévu : c’est juste l’éditeur de texte ed en mode non interactif et sur un flux (la première lettre est pour stream) et non un bout de périphérique bloc… Donc ça fait beaucoup plus que juste de la substitution, et plus simplement que beaucoup d’autres alternatives que j’ai vu par la suite.
          Et sinon, tu peux faire de la substitution pareillement avec AWK …ou ton langage de script favori.

          Le problème, c’est que parfois les besoins évoluent.
          […]

          Utiliser le bon outil qui répond au besoin du moment. Ne pas optimiser prématurément (s’applique aussi au fait de vouloir adresser trop tôt des besoins dont on ignore l’existence.)
          Jean est fier de son vélo qu’il utilise pour faire ses courses. Paul lui explique que les besoins vont évoluer et qu’en plus il est fort probable que la guerre fasse des jolis sentiers des tas de ruines. Donc il lui préconise de laisser tomber le vélo et de s’équiper en char…

          “It is seldom that liberty of any kind is lost all at once.” ― David Hume

    • [^] # Re: AWK, c’est comme Perl en moins bien

      Posté par  . Évalué à 3 (+1/-0).

      Accessoirement, Perl est 5x plus lent

      $ time awk '/co/ { }' 1m.csv.gz
      
      2560 0.14
      
      $ time perl -ane 'if (/co/) { }' 1m.csv.gz
      
      5376 0.70
      • [^] # Expressions régulières

        Posté par  (site web personnel) . Évalué à 2 (+1/-0). Dernière modification le 23 octobre 2025 à 00:33.

        Et est‐ce que ça compense le temps que tu mets à écrire [:digit:] plutôt que \d dans une expression régulière plus complexe ?

        Perl est considéré comme une référence concernant les expressions régulières pour leur richesse et beaucoup ont choisi de le copier, par exemple Python ou cette bibliothèque pour C qui y fait explicitement référence.

        Je me fiche un peu de savoir de savoir si grep ou AWK sont plus rapides, vu que leurs limitations me gênent rapidement.

        Je sais qu’il y en a qui ont fait une implémentation sensiblement plus rapide qui reprend presque toutes les possibilités de Perl sauf une ou deux identifiées comme sources de ralentissement. Je ne me rappelle malheureusement plus laquelle c’est (peut-être une implémentation en Go voire celle fournie avec le langage).

        Ça vaut sûrement plus le coup que celle d’AWK. D’un autre côté, si c’est un langage compilé, on ne va pas l’utiliser pour une petite commande rapide en une ligne…

        Prendre une bonne disposition : beop.free.fr

        • [^] # Re: Expressions régulières

          Posté par  (Mastodon) . Évalué à 3 (+0/-0).

          Et est‐ce que ça compense le temps que tu mets à écrire [:digit:] plutôt que \d dans une expression régulière plus complexe ?

          Tout dépend le nombre de fois que tu exécute le même script. Le gain en temps c'est du gain en énergie. Si une seule petsonne optimise ça sert peu mais si l'effort est commun ça peut être significatif.

          Après on peut de demander si ça ne vaut pas le coup de compiler un exécutable si le code évolue peu.

          • [^] # Re: Expressions régulières

            Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0).

            Il me semble que ce n’est pas exactement la même chose et qu’il faut justement privilégier la classe…

            “It is seldom that liberty of any kind is lost all at once.” ― David Hume

            • [^] # Re: Expressions régulières

              Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0).

              C’est par rapport à ceci (la graisse est mon fait) :

              Within a bracket expression, the name of a character class enclosed in ‘[:’ and ‘:]’ stands for the list of all characters belonging to that class. Standard character class names are: alnum, digit, punct, alpha, graph, space, blank, lower, upper, cntrl, print, xdigit.
              These stand for the character classes defined in ctype(3). A locale may provide others.

              Donc, [[:digit:]] ou \d va me permettre de trouver avec nos écritures romanes tout ce qui est « 0123456789 ». Mais dans un système bien configuré pour utiliser l’écriture arabique, ce même [[:digit:]] permet de capturer tout ce qui est « ٠١٢٣٤٥٦٧٨٩ » alors que toutes les descriptions que j’ai lue de \d indiquent que ça ne traite que le premier cas.
              Enfin, si j’ai bien compris.

              “It is seldom that liberty of any kind is lost all at once.” ― David Hume

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.