Ce chapitre est destiné à répondre aux problèmes les plus fréquemment posés lors de l'écriture d'un script perl. Si vous ne trouvez pas la réponse à la question que vous vous posez, consultez la FAQ que vous trouverez sur http://www.perl.com/perl/.
Il existe deux manières pour effectuer cela : on peut d'abord utiliser des backquotes comme en shell. Par exemple :
$pwd = `/bin/pwd`; chop($pwd);
La deuxième manière, qui offre plus de possibilités, est la suivante :
($pid = open(PIPE, "/bin/ps -a |")) or die "Error: $!\n"; (kill 0, $pid) or die "ps invocation failed"; while (defined($line = <PIPE>)) { ...; } close(PIPE);
Cette manière d'opérer se retrouve également dans l'opération inverse, qui consiste à envoyer des données sur l'entrée standard d'un programme :
($pid = open(PIPE, "| /usr/ucb/ftp")) or die "Error: $!\n"; (kill 0, $pid) or die "ftp invocation failed"; print PIPE "open ftp.enst-bretagne.fr\n"; ...; close(PIPE);
Dans l'exemple présenté ci-dessus, il serait plus intéressant de pouvoir envoyer des données sur l'entrée standard en même temps que de récupérer des données sur la sortie standard. Ceci est possible grâce à un module fourni dans la distribution de perl : IPC::Open2.
Pour effacer un fichier, il est inutile de faire un appel au programme /bin/rm, comme beaucoup de personnes le font. Il existe une instruction unlink qui appelle la fonction C du même nom et supprime le fichier dont le nom est passé en paramètre.
En revanche, il n'existe pas d'instruction pour copier un fichier. Le méthode conseillé est d'utiliser le module File::Copy, ou bien on peut écrire soi-même les quelques lignes pour copier le fichier :
open(SOURCE, "<$source") || die "Error: $!\n"; open(DEST, ">$dest") || die "Error: $!\n"; while ($len = sysread(SOURCE, $buffer, 512)) { syswrite(DEST, $buffer, $len); } close(DEST); close(SOURCE);
Il existe en perl la possibilité d'utiliser les here-documents de manière analogue au shell, afin de faciliter l'écriture de longues chaînes de caractères. On utilise une chaîne de caractères particulière pour délimiter un bloc de texte, et on peut ainsi insérer de manière lisible des variables multilignes, ou encore utiliser des guillemets sans avoir à les précéder d'un backslash.
C'est très pratique par exemple pour écrire des scripts CGI :
print <<"FIN"; Content-type: text/html <HTML><HEAD> <TITLE>Erreur</TITLE> </HEAD><BODY> <H1>Erreur</H1> $err_msg </BODY></HTML> FIN
L'utilisation de guillemets simples permet d'éviter que les variables ne soient interpolées à l'intérieur de la chaîne.
Il existe deux pièges à éviter lors de l'utilisation de ce mécanisme : il ne faut surtout pas oublier le point-virgule après le délimiteur de bloc, ce qui constitue une erreur de syntaxe.
De plus, l'identificateur de fin de bloc doit être impérativement au début de la ligne.
Ceci constitue une action fréquemment rencontrée lors de l'écriture de scripts. La solution dépendra de la taille de la liste.
Une première solution est de parcourir le tableau et de tester chaque valeur successivement :
$in = 0; for $val (@liste) { if ($item eq $val) { $in = 1; last; } }
On peut également utiliser la fonction grep qui effectue un test sur tout un tableau, et renvoie l'ensemble des éléments du tableau qui ont validé le test :
@elements = grep($_ eq $item, @liste);
Enfin, et c'est la méthode la plus rapide, mais à n'utiliser que sur des listes de taille raisonnable, on peut utiliser un tableau associatif et tester si la valeur recherchée est une clé du tableau :
for $cle (@liste) { $hash{$cle} = 1; } # ce qui peut s'ecrire de maniere moins lisible (mais plus # courte a taper...) : # %hash = map( ($_, 1), @liste ) if (defined($hash{$item})) { $in = 1; }
Il existe de nombreuses options de ligne de commande qui permettent entre autre d'effectuer des actions simples.
Une liste complète de ces options se trouve dans la section perlrun du manuel.
Pour effectuer ceci, nous utilisons deux options de ligne de commande. L'option -p lit chaque fichier dont le nom est passé en argument au script, effectue une action sur chaque ligne puis l'affiche sur la sortie standard.
Par exemple, le script suivant affichera le fichier passé en paramètre après avoir converti toutes les lignes en minuscules.
#!/usr/local/bin/perl -p lc($_);
Il peut s'écrire également en ligne de commande de la manière suivante :
perl -p -e 'lc($_)' fichier.txt
L'option -e permet d'indiquer une expression à évaluer. Plusieurs options -e peuvent être fournies au script, auquel cas toutes les expressions passées seront concaténées, et le script résultant exécuté.
L'utilisation de ces options est particulièrement pratique lorsqu'elles sont combinées à l'option -i (qui signifie in-place).
Cette option modifie le comportement de l'option -p : chaque ligne, au lieu d'être réecrite sur la sortie standard, est écrite dans un fichier, qui sera ensuite recopié à la place du fichier original.
Si l'on fournit un paramètre à cette option (-i.bak), une copie du fichier original sera effectuée, en ajoutant l'extension précisée au nom du fichier, avant de procéder à la suite des opérations.
En pratique, si l'on veut substituer un mot pour un autre dans plusieurs fichiers, il suffit d'écrire :
perl -pi.bak -e 's/oldstr/newstr/g' *
Soit un fichier au format HTML dont on veut extraire toutes les références. Ceci peut être effectué en une seule ligne, grâce à l'option -n qui lit chaque ligne des fichiers précisés, mais sans la réécrire ensuite. Attention, on fait ici l'hypothèse que toutes les URLs tiennent sur une seule ligne, ce qui n'est pas forcément le cas. Pour des gestions plus complètes, utilisez les modules adéquats (HTML::*).
perl -ne 'print $1, "\n" while (/\/ig)' *.html
Un deuxième exemple :
ypcat passwd | perl -ne 'print $2, "\n" if (/(.+?:){4}(.+?):.+\/bin\/sh/)'
perl permet un accès très facile aux fichiers de base de données au format dbm. Il est possible d'établir une correspondance entre un tel fichier et un tableau associatif. Par la suite, les opérations effectuées sur le tableau associatif seront répercutées sur la base de données.
use NDBM_File; tie(%HIST, NDBM_File, '/usr/lib/news/history', 1, 0); print keys(%HIST), "\n"; delete $HIST{"cle"}; $HIST{"cle2"} = "element"; untie(%HIST);
Une caractéristique intéressante de perl est son débuggueur intégré, qui est d'ailleurs lui-même écrit en perl.
Il est invoqué par l'option -d :
perl -d script.pl
On peut également l'utiliser pour faire exécuter interactivement des commandes par l'interpréteur :
perl -d -e 1
Lors du débuggage d'un script, deux autres options sont également intéressantes.
-c vérifie uniquement la syntaxe du script, sans chercher à l'exécuter.
-w est beaucoup plus intéressant : il affiche divers messages concernant les variables non initialisées, ou bien utilisées une seule fois, etc.
Donc, pour tester la validité syntaxique d'un script, avant de le lancer, il suffit de le tester avec la commande
perl -wc script.pl