next up previous contents index
Next: Programmation en C Up: Linux, commandes, outils et Previous: Programmation avec Tk

   
Programmation en Perl

Les scripts Perl est souvent utilisés par les pages Web. Ils permettent de rendre interactive une simple page Web. Ce langage est très simple et très polyvalent, si bien qu'il existe souvent plusieurs facons d'effectuer la même tâche. Par exemple, les expressions :

while ($_ = <STDIN>) {print;}

while (<STDIN>) {print;}

for (;<STDIN>;) {print;}

print while $_ = <STDIN>;

print while <STDIN>;

sont toutes équivalentes. Cette polyvalence peut être génante, mais c'est à l'utilisateur de choisir la forme qui lui plaît.

Le script qui suit montre comment utiliser des variables, des tablaux, comment effectuer des opérations sur les variables, qu'elles soient des chiffres ou des chaînes de caractères...

Noter que le chemin indiqué au début du script peut aussi etre /usr/local/bin/perl sur certains systèmes. Pour s'en assurer, la commande which perl donnera le bon chemin.

#!/usr/bin/perl

# Script de demonstration de perl.

print "\n";


#-----------#
# variables #
#-----------#


# Scalaires.

$age=22;
$dents=28;
$total=$age+$dents;
$utilisateur="Mathieu";

print "$utilisateur a $age ans, $dents dents et $total age+dents\n";
print '$utilisateur a $age ans, $dents dents et $total age+dents\n';

undef $nom;

#$nom="moi";

if (!(defined $nom)) {
    print "variable nom non définie !\n" ;
}

# Matricielles.

@experience=(2, 5, 3);
@languages=("Perl", "Tcl/Tk", "Bash");
$nombre=@languages;

print "il connait $nombre languages Linux :\n";
for ($i=0; $i<$nombre; $i++) {
    print "$languages[$i] depuis $experience[$i] semaines\n";
}

print "dernier indice de la matrice \@languages : $#languages\n";
print "\@languages : (@languages)\n";
print "\n";

# Matricielles associatives.

%def_partitions=(1, "\/Dos", 4, "\/", 5, "\/home", 6, "\/usr");

foreach $i (1, 4, 5, 6) {
    print "partition $i point de montage $def_partitions{$i}\n";
}
print "\n";

%def_espace=("\/Dos", 701, "\/", 94, "\/home", 48, "\/usr", 756);

foreach $i ("\/Dos", "\/", "\/home", "\/usr") {
    print "partition $i espace disque occupe $def_espace{$i}M\n";
}

print "\n";
print "Appuyer sur une touche pour continuer...\n";

<STDIN>;

# Une matrice associative contenant les variables d'environnement est
# definie par Perl : %ENV.

#print "PATH=$ENV{PATH}\n";
print "\n";

# @ARGV contient les arguments -> $ARGV[0], ARGV[1]...
print "$ARGV[0] $ARGV[1]\n";

print "id. du script : $$\n";
print "id. de l'utilisateur utilisant le script : $<\n";
print "etat renvoye par le dernier appel system : $?\n";
print "argument par defaut : $_\n";
print "nom du script : $0\n";

# cf. (pages de manuel, par exemple) "delete", "each", "keys", "value"
# qui sont des operateurs sur les matrices.

# "shift" permet de passer a l'argument suivant.

$premier=shift @languages;
print "premier argument de \@languages : $premier\n";

$second=shift @languages;
print "second argument de \@languages : $second\n";


#------------#
# operateurs #
#------------#


# Pour les chaines de caracteres, les operateurs (==, !=, <, >, <=, >=)
# sont remplaces par (eq, ne, lt, gt, le, ge).

# Valeur de retour de "cmp" : -1 si <, 1 si >, 0 si =. C'est l'ordre
# alphabetique qui est pris en compte.

if (("ab" cmp "ac")==-1) {print "<\n";}
if (("ad" cmp "ac")==1) {print ">\n";}
if (("ad" cmp "ad")==0) {print "=\n";}

# "**" designe la puissance.

$x=2;
$y=3;
$z=$x**$y;

print "x=$x y=$y x^y=$z\n";

# Pour elever au carre...

$y **=2; print "y*y=$y\n";

# "()" initialise a 0.

print "\@experience : (@experience)\n";
@experience=();
print "\@experience : (@experience)\n";

# "." concatene 2 chaines.

$chaine1="Salut";
$chaine2=" le monde";
$message=$chaine1.$chaine2;

print "$message\n";

# "x=" repete un certain nombre de fois une operation.

$marqueur="*";
$marqueur x= 14;

# Affiche 14 '*'.

print "$marqueur\n";

# ".." designe un operateur de plage.

@chiffres=(1..9);
@alphabet=("a".."z");

print "@chiffres\n";
print "@alphabet\n";
print "\n";

$user="mathieu";
#$user="root";

if ($user eq "root") {
print "root c'est sympa...mais dangereux !\n";
}
else {
print "Desole $user, vous devez etre \"root\" !\n";
}

Le script qui suit montre comment utiliser les boucles, les écritures ou lectures dans un fichier, les appels systèmes, les expressions rationnelles...

#!/usr/bin/perl

# Script de demonstration de perl.

print "\n";


#----------------------------#
# if...else...elsif...unless #
#----------------------------#


# Lecture clavier.

print "Entrer un chiffre 0<chiffre<10 :\n";

$chiffre=<STDIN>;

# Supprime l'interligne apres la derniere ligne.

chop $chiffre;

if ($chiffre >=7) {
    print "chiffre >=7\n";
}
elsif ($chiffre >=4 && $chiffre < 7) {
    print "4<=chiffre<7\n";
}
elsif ($chiffre >=2 && $chiffre<4) {
    print "2<=chiffre<4\n";
}
else {
print "chiffre <1 ou chiffre >10\n";
}

# unless execute la commande si la condition est fausse...

unless ($user eq "root") {
    print "mais puisque je te dis que tu n'es pas root, $user !\n";
}


#-------------------#
# while for foreach #
#-------------------#


# 4 facons d'utiliser une boucle...

# Facon 1.
# "@chiffres" est un tableau.

@chiffres=(1..9);

while (@chiffres) {
    $i=shift @chiffres;
    print "$i\n";
}

# Facon 2.

$somme=0;
$i=0;

while (1) { # toujours verifie.
    $i++;
    if ($i==5) {next}; # saute a l'iteration suivante.
    if ($i>10) {last}; # fin de boucle.
    $somme+=$i;
}
print "somme 1 -> 10 sans 5 : $somme\n";

# Facon 3.

for ($i=0, $somme=0; $i<=20; $i++) {
    $somme+=$i;
}
print "somme 1 -> 20 : $somme\n";

# Facon 4.

$somme=0;
$i=0;

foreach $i(1..30) {$somme+=$i;}
print "somme 1 -> 30 : $somme\n";

$somme=0;
$i=0;

foreach $i(1,30, 120) {$somme+=$i;}
print "somme 1+30+120 : $somme\n\n";


#----------------#
# appels systeme #
#----------------#


$prompt="Commande (ou \"exit\") : ";

print "essai d'appel systeme 1\n";
print $prompt;

while (<STDIN>) {
    chop;

# "$_" designe la derniere entree (ici <STDIN>).

    if ($_ eq "exit") {last;}
    system $_;
unless ($?==0) {print "Erreur d'execution : $_\n";}

print $prompt;
}

# Pareil en utilisant fork et exec (cree un processus fils qui execute
# la commande demandee, puis ferme le processus fils et revient au
# processus pere).

print "essai d'appel systeme 2 (en utilisant fork et exec)\n";
print $prompt;

while (<STDIN>) {
    chop;
    if ($_ eq "exit") {last;}
    $statut=fork;
    if ($statut) {
        wait; # Attends dans le processus pere la fin du processus fils.
        print $prompt;
        next;
    }
    else
    {
        exec $_}
}

print "\n";

# Ouverture du fichier "/etc/passwd" en lecture.
# Ouverture du fichier "sortie" en ecriture( ">").

open (fichier, "/etc/passwd");
open (out, ">sortie");

# Tant qu'il y a une ligne dans le fichier ouvert...

while(<fichier>) {

# Si un utilisateur n'a pas de shell, on l'inscrit dans le fichier
# de sortie.

    if ($_ =~ /:\n/) {print out $_}
}

close fichier;
close sortie;


# Envoi d'un mail.
# Si le nom du fichier dans "open" est precede de "|", ce nom est
# considere comme une commande.

foreach ("root", "mathieu"){
    open (message, "| mail -s essai $_");
    print message "Ca marche...\n";
    close message;
}

# Si le nom du fichier dans "open" est suivit de "|", ce nom est
# considere comme une commande qui sera executee.

open (pl, "ls -l *.pl |");
while (<pl>) {
    print $_;
}

print "\n";

# Exemple de fonction.
# Les arguments passes a la fonction sont stoques dans la matrice "@_".

sub hello {

# Copies locales des arguments.

    local($premier, $dernier)=@_;
    print "Bonjour, $premier, $dernier\n";
}

$a=$ENV{USER};
$b=root;

# Appel de la fonction "hello".

&hello($a,$b);

# Appel du script "salut.pl" qui contient la definition de la fonction
# "salut".

require 'salut.pl';

# Appel de la fonction "salut".

&salut($a,$b);

print "\n";


#-------------------------#
# expressions rationelles #
#-------------------------#


# Les expressions rationelles sont rangees entre 2 "/".
# Le signe "=~" signifie : "si c'est equivalent du modele".
# Le signe "!~" signifie : "si c'est different du modele".


print "Essai expressions rationnelles...\n";
print "quit pour quitter\n";

while (<STDIN>) {
    print "le texte suivant doit comporter 3 espaces au debut
(quit pour sortir)\n";
    if ($_ =~ /^{3}\s/) {print "3 espaces au debut...\n\n"}
    if ($_ !~ /\)\n/) {print "ne se termine pas par ')'...\n\n"}
    if ($_ =~ "quit") {exit}
}

Le fichier salut.pl demandé par le script précédent pourrait ressembler à cela :

# Exemple de fichier annexe Perl.

# Ce fichier contient simplement une fonction.
# Pour utiliser cette fonction, mettre dans le script Perl :
#   require 'salut.pl';
# avant l'appel de la fonction "salut".

sub salut {
    local($premier, $dernier)=@_;
    print "Salut, $premier, $dernier\n";
}
1; # valeur de retour.

A propos des expressions rationnelles, beaucoup utilisées par Perl : celles ci permettent de rechercher une chaîne de caractère suivant un modèle précis. Des exemples de modèles ont déjà été évoqués à propos de la commande grep (voir section 1.3 page [*]). Le tableau 7.1 présente d'autres modèles pouvant être utilisés.


 


 
Tableau 7.1: Règles d'interprétation des caractères contenus dans une expression rationnelle.
Expression Signification

.

Correspond à tout caractère, sauf le caractère interligne.

x*

Correspond à 0 ou plusieurs occurrences du caractère `` x''.

x+

Correspond à 1 ou plusieurs occurrences du caractère `` x''.

x?

Correspond à 0 ou 1 occurrences du caractère `` x''.

x[3]

Correspond à 3 occurrences du caractère `` x''.

x[3,]

Correspond à au moins 3 occurrences du caractère `` x''.

x[,7]

Correspond à au plus 7 occurrences du caractère `` x''.

x[3,7]

Correspond à au moins 3 et au plus 7 occurrences du caractère `` x''.

[...]

Correspond à n'importe lequel des caractères mis entre crochet.

$

Correspond à la fin d'une ligne.

\0

Correspond au caractère nul.

\b

Correspond à un retour arrière.

\B

Correspond à tout caractère d'un mot, sauf le premier et le dernier.

\b

Correspond au début ou à la fin d'un mot (s'il n'est pas entre crochets).

\cX

Correspond à Ctrl-x.

\d

Correspond à un seul chiffre.

\D

Correspond à un caractère non numérique.

\f

Correspond à un saut de page.

\n

Correspond à un caractère retour ligne.

\147

Correspond à la valeur octale 147.

\r

Correspond à un retour chariot.

\S

Correspond à un caractère d'espacement non blanc.

\s

Correspond à un caractère d'espacement blanc ( espace, tabulation, interligne).

\t

Correspond à une tabulation.

\W

Correspond à un caractère non alphanumérique.

\w

Correspond à un caractère alphanumérique.

\18f

Correspond à la valeur hexadécimale 18f.

^

Correspond au début d'une ligne.

 

 


  Le script qui suit montre un exemple concret d'emploi d'expression rationnelles pour reformater la sortie d'une commande en n'affichant que certains éléments. La syntaxe pour utiliser ce script est :

ls -l | taille.pl

Voici le script :

#!/usr/bin/perl

# taille.pl : exemple d'emploi d'expression rationnelle.

# taille.pl met dans le tableau @champs le resultat de la commande ls -l.

# Syntaxe : ls -l | taille.pl.

# @champs est le tableau dans lequel les elements sont ranges (la
# numerotation commence a 0).

# La fonction split divise chaque ligne en champs.

# "/\s+/" designe un ou plusieurs espaces comme separateurs.

# "$_" represente la ligne d'entree derniere (ici <STDIN>).

while (<STDIN>) {
    @champs = split(/\s+/, $_);
    $taille = $champs[4];
    $nomfichier = $champs[8];
    printf " \"%s\" a une taille de %d octets\n", $nomfichier, $taille;
}

Le résultat pourrait ressembler à ca :

"entree" a une taille de 187 octets
"essai.pl" a une taille de 869 octets
"logintime" a une taille de 1221 octets
"salut.pl" a une taille de 320 octets
"simple.perl" a une taille de 4137 octets
"sortie" a une taille de 0 octets
"taille.pl" a une taille de 634 octets
"var.perl" a une taille de 3212 octets

C'est quand même plus zoli, non ?

Le script qui suit montre quelque possibilités de programmation avancée avec Perl. On peut malgrès tout facilement comprendre ce qu'il fait. Il montre à quel point il peut être facile de traiter un problème avec Perl.

#!/usr/bin/perl

# Ce script montre quelque possibilites de programmation avancee
# avec Perl...

print "Exemple d'emploi d'expressions rationnelles...\n";
print "----------------------------------------------\n";

print "'Mathieu' marche...\n";

# $name commence par Mathieu ("^"), en maj. ou min. ("i") avec un espace
# apres Mathieu ("\b").

$name=<STDIN>;
    if ($name =~ /^Mathieu\b/i) {
        print "Bonjour, Mathieu\n";
    } else {
        print "desole...\n";
    }

# Enleve ("s") tout apres (".*") un char different d'une lettre, d'un
# chiffre ou d'un chararactere souligne ("\W").

$name=~s/\W.*//;

# Transcrit en minuscules.

$name=~tr/A-Z/a-z/;

print "$name\n";

# Pour tous les fichiers se terminant par ".secret";
while ($filename=<*.secret>) {
    open (liste, $filename);

# S'il date de moins de 7 jours... on recupere les lignes

    if (-M liste<7) {
        while ($name=<liste>) {
            chop ($name);
            print "$filename -> $name\n";
        }
    }
    close(liste);
}

# "getpwuid" contient les champs du fichier /etc/passwd.

@pass=getpwuid($<);

foreach $i (0..8) {
    print "i=$i, $pass[$i]\n";
}

Le script qui suit montre comment reformater de facon soignée la sortie d'une commande comme last, qui indique pour chaque login le nom de l'utilisateur et le temps passé. Le script logintime aditionne le temps passé dans chacune de ces sessions et affiche le résultat sous forme d'un tableau.

#!/usr/bin/perl 


# Fichier logintime.
# Ce script reformate la sortie de la commande last.

# Utilisation : last | logintime.

# La sortie s'affichera sous forme de tableau avec en tete.


# Tant qu'il y a quelque chose a lire...
# cherche une ligne et sauvegarde le nom et le temps.

while (<STDIN>)
  {
  if (/^(\S*)\s*.*\((.*):(.*)\)$/)
      { $heures{$1}+= $2;
        $minutes{$1} += $3;
        $logins{$1}++;
      }
  }

foreach $user (sort(keys %heures))
{
  $heures{$user} += int($minutes{$user} / 60);
  $minutes{$user} %= 60;

# Facon simple d'afficher la sortie :

#  print "Utilisateur $user, temps total ";
#  printf "%02d:%02d, ", $heures{$user}, $minutes{$user};
#  print "total des sessions $logins{$user}.\n";

# Facon avec formatage de page :

# Conversion de la chaine numerique en chaine de caractere.

$letemps = sprintf("%02d:%02d", $heures{$user}, $minutes{$user});

# Demande d'utilisation du format de sortie definit pour afficher
# une ligne.

write;

# Definition du format du haut de la page et du format de sortie.
# Le "." marque la fin de la definition de STDOUT_TOP et STDOUT.
#
# "@<<<<<" justifie le texte a gauche et "@#####" denote une
# valeur numerique.

format STDOUT_TOP =
 Utilisateur   Temps total   Nombre de sessions
------------- ------------- --------------------
. 


format STDOUT =
@<<<<<<<<<<<< @<<<<<<<<<<<< @#######
$user,        $letemps,     $logins{$user}
. 
}

Ainsi, le résultat pourrait ressembler à ceci :

 Utilisateur   Temps total   Nombre de sessions
------------- ------------- --------------------
mathieu       31:36               28
root          00:43               19

Une fois de plus, de facon très simple on a réussit à effectuer une tâche qui aurait pu être compliquée à réaliser dans un autre langage.


next up previous contents index
Next: Programmation en C Up: Linux, commandes, outils et Previous: Programmation avec Tk
MATHIEU DECORE
1999-11-03
Merci de me dire ce que pensez de ce document.