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

    Posté par (site web personnel) . En réponse au journal écriture d'un script AWK de transformation de Markdown en HTML. Évalué à 10.

    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