PGP Copertina Indice |
Articoli
Introduzione al Perl
Tutti hanno sentito almeno una volta nella loro vita di utenti di
computer l'esigenza di scrivere qualche programmino per automatizzare
le operazioni che si compiono quotidianamente.
Usare per queste operazioni elementari un linguaggio di
programmazione compilato è un po' dispersivo, specialmente perché
i linguaggi convenzionali non offrono dei servizi di livello
sufficientemente alto, o almeno non così alto da rivaleggiare con il
set di comandi che un utente Unix si trova ad usare nella pratica di
tutti i giorni.
Per questo si sono sempre usati i cosiddetti shell script:
dei file
che contengono sequenze di quei comandi (o programmi se preferite) che
si trovano in qualsiasi sistema Unix.
Per alcuni scopi questi shell script sono sufficienti, ma se si
vogliono eseguire dei compiti complessi, come l'elaborazione di file,
a volta si fatica un po' troppo per quello che si vuole ottenere.
Esistono però altri modi di scrivere in fretta un programmino che
analizzi certi file ed emetta un risultato. Entrano in scena allora
sed (stream editor) che automatizza le operazioni
che è possibile
compiere con un editor su di un file ed awk, un
linguaggio basato sul
riconoscimento di pattern all'interno di un file di testo e sulla loro
elaborazione.
Con l'aiuto di questi piccoli tool è stato possibile per la maggior
parte degli utenti creare dei programmi di inusitata complessità,
ma... C'è sempre un ma.
Usando i comandi degli shell script, il sed o l'AWK si può operare
solo su file di testo e neanche tanto agevolmente, tanto è vero che
spesso sed e AWK sono usati all'interno di shell script che comunque
ne dirigono le operazioni. Oltre tutto i programmi ottenuti in questo
modo sono spesso lenti e se vanno bene per elaborare piccoli file in
modo semplice, mostrano di essere inadeguati per operazioni complesse
o per operare su file molto grandi.
Il Perl è stato creato per venire incontro
all'esigenza di avere
un linguaggio di programmazione di uso generale, potente, veloce
nell'esecuzione e nella scrittura dei programmi. Sebbene al momento
della sua nascita fosse un linguaggio orientato all'elaborazione dei
testi (da cui il suo nome: Practical Extraction Report Language), il
Perl è in grado di trattare con altrettanta facilità ed assoluta
trasparenza anche file binari.
Punti di forza del Perl (almeno all'epoca delle sue origini: con lo
sviluppo esplosivo che ha subito ora ne ha molti altri) erano le regular
expression, estremamente potenti ed articolate e il sistema di reporting
che consente di disegnare il layout della pagina.
Ora il Perl viene usato per gli scopi più diversi: utility di
sistema, accesso a database, networking, programmazione CGI e grafica.
Questo linguaggio è ora disponibile su moltissime piattaforme, da
Unix (la sua piattaforma originaria) a Plan 9, VMS, QNX, OS/2, Amiga
e Win32.
La caratteristica più peculiare di questo linguaggio è la libertà di espressione che consente al programmatore: è un linguaggio ``ridondante'' dal punto di vista delle strutture logiche e di flusso. Questo rende più facile per un programmatore accostarsi al linguaggio, qualunque siano state le sue esperienze passsate. Il motto del Perl è ``There's more than one way to do this'' (c'è più di un modo per farlo) e il suo autore tiene molto a questa forma di libertà che contrasta così tanto con la pratica comune di creare linguaggi parchi di istruzioni, con il minor numero possibile di strutture, seguendo una ricerca estetica di semplicità.
A cosa somiglia il Perl?
Questa è una domanda che ha una risposta insolitamente lunga: ha
dentro di sé caratteristiche prelevate da numerosi linguaggi.
Chi ha programmato con gli shel script riconoscerà qualcosa della
Bourne shell e della C shell, così come certe
altre istruzioni
ricordano il sed, l'AWK, il C, passando
addirittura per alcuni dialetti del BASIC.
In realtà, come ho già detto, questo è un punto di forza, perché
chi ha un minimo di esperienza di programmazione in qualsiasi
linguaggio può diventare produttivo molto rapidamente.
Il Perl è un linguaggio interpretato: non necessita di dichiarazioni
di variabili: a secondo dell'uso che se ne fa, le variabili vengono
trasformate nel tipo necessario. Ad esempio si può assegnare un
valore numerico ad una variabile, moltiplicarlo per due e
concatenargli una stringa.
Questa caratteristica in realtà è quanto ci si possa già aspettare
da un linguaggio interpretato. Ma il Perl è più di un linguaggio
interpretato: in realtà quando si lancia un programma Perl, il testo
del programma viene letto e compilato, viene quindi eseguito ad una
velocità molto elevata.
Il Perl è molto ottimizzato (è il frutto di anni di lavoro da parte
di hacker di tutto il mondo), ma per i fan del codice binario è
disponibile in beta un compilatore ``reale'' e presto sarà disponibile
anche un compilatore in grado di tradurre il Perl in byte code Java...
In sostanza la velocità del Perl non è un problema, perché il
linguaggio dispone di una espressività di livello molto alto, che
consente di pilotare facilmente routine interne all'interprete,
come ad esempio il meccanismo di ricerca e sostituzione all'interno di
una stringa.
Una caratteristica che rende rapida la scrittura ``fisica'' di un programma Perl è che praticamente tutte le funzioni hanno dei default, ad esempio lavorano sulla riga corrente del file di input o sul valore della variabile di default, quella che poi contiene la riga appena riga.
Negli ultimi anni sono state aggiunte al Perl delle caratteristiche
sofisticate: le possibilità di immagazzinare in variabili dei
riferimenti ad altre variabili o subroutine e la possibilità di
usare ``moduli'', il termine con cui si designano in Perl le classi.
In realtà un modulo non è niente più di una raccolta di
subroutine incapsulate in un package (l'analogo ante litteram dei
namespace del C++, presente da anni nel linguaggio) e di una
reference di tipo particolare che conserva al suo interno, oltre ai
dati degli oggetti, l'indicazione della classe a cui appartiene e
attraverso la quale si può accedere ai dati e funzioni membro del
modulo.
L'object orientation nel Perl non è basata sull'inaccessibilità dei
dati e delle funzioni private delle classi, ma su un patto democratico
(diciamo una policy) tra il creatore del modulo e i suoi utenti.
L'autore documenta i dati e un'interfaccia che l'utente farebbe meglio
a rispettare, perché il resto delle funzioni interne non è detto che
resti invariata tra una versione e l'altra o necessita di una comprensione
troppo approfondita delle operazioni svolte dal modulo per poter essere
usata in modo da non provocare malfunzionamenti.
Parlo di patto o accordo, perché spesso si tratta di un vero accordo
bilaterale e non di un dictat da parte dell'autore. Questa
affermazione introduce un discorso sul metodo con cui procede lo
sviluppo del Perl e dei suoi moduli.
Questo metodo di sviluppo ricorda molto da vicino quello adottato per
Linux: i sorgenti del linguaggio sono liberamente disponibili e sono
coperti da una Artistic License, che è meno
restrittiva della GPL della GNU.
Chiunque può partecipare allo sviluppo: gli autori sono
frequentatori accaniti dei vari newsgroup dedicati al linguaggio ed è
possibile dialogare con loro, porre quesiti, ma anche fare richieste,
che, se ragionevoli, vengono prese in considerazione.
In pratica la discussione sulla definizione del linguaggio è sempre
stata pubblica e chiunque ne abbia voglia può parteciparvi.
Lo stesso succede per i moduli (ormai se ne contano a centinaia). Il
luogo deputato alla discussione delle caratteristiche dei nuovi
moduli è sempre un newsgroup. Non è raro assistere alla discussione
pubblica su ciò che un modulo deve fare e su come sarebbe meglio
scriverlo.
Insomma lo spirito di collaborazione, l'estrema dinamicità dello
sviluppo e l'eccellenza dei risultati ricorda molto da vicino ciò
che succede nella comunità Linux.
Questa situazione ha favorito il moltiplicarsi dei moduli, che
coprono ora ogni aspetto dell'informatica, trasformando il Perl da un
linguaggio di text processing in un linguaggio general purpose.
Seguono alcuni esempi di codice Perl particolarmente interessanti, utili o folkloristici, trovati nei newsgroup dedicati al Perl. (comp.lang.perl.*).
1: #!/usr/bin/perl -n00
2:
3: while(m{
4: <\s*A
5: \s+HREF
6: \s*=\s*
7: (["'])
8: (.*?)
9: \1
10: .*?>
11: }xsgi)
12: {
13: print "$2\n";
14: }
Questo programmino estrae tutti i link contenuti in un file HTML e li
stampa sullo schermo.
Nella prima riga vengono passate anche delle opzioni al perl:
-n
significa esegui il programma che segue per ogni riga letta e
-00
significa leggi il file di input un paragrafo alla volta.
Così se questo programma viene chiamato ``estrai_link'', basta dare il
comando:
$ estrai_link pagina.html
per avere come output tutti i link presenti nel file. Il programma è composto da un ciclo while che testa il risultato di un pattern matching: se la stringa è stata trovata nella riga (paragrafo) corrente, allora viene stampata. La regular expression m{...} identifica una espressione HTML del tipo:
<a href="...">
Nelle righe dalla 4 alla 6 si cerca la parte <a href= del link. Vengono usate delle espressioni del tipo \s , che rappresentano la classe dei caratteri spazio (nel nostro caso sono lo spazio, il tab e l'andata a capo).
La riga 7 cerca un carattere appartenente all'insieme ["'] e viene conservata memoria del carattere trovato (le parentesi tonde individuano una match da ricordare). Questo perché la stringa che compone il link può essere circondata da singoli o doppi apici: se il primo apice trovato è singolo, lo sarà anche quello di chiusura e lo stesso succede per i doppi apici. In pratica in HTML sono possibili le due scritture:
<a href='...'>
e
<a href="...">
La riga 8 fa il match del link e ne ricorda la stringa (è tra parentesi tonde). Il quantificatore, cioè i metacaratteri che indicano quante volte è ripetuto il carattere cercato è di tipo ``*?''. Questo vuol dire che nella ricerca della stringa individuata dalla regular expression bisogna sempre tenere presente il carattere successivo da trovare e ci si deve fermare quando lo si trova. In pratica raccogliendo i caratteri che formano il link mi devo fermare quando trovo l'apice successivo, segnalato nella riga 9 dalla variabile \1, che contiene il carattere letto nell'espressione contenuta nella prima coppia di parentesi tonde (["']).
La riga 10 dice che possono seguire una serie di caratteri, terminati comunque dal carattere '>'.
Nella riga 11 si dice che la regular expression è di tipo extended (x): gli spazi e le andate a capo, inserite per migliorarne la leggibilità devono essere tralasciati nel pattern matching. La regular expression deve essere inoltre case insensitive (i), deve poter individuare tutti i link presenti nella riga di input e non solo il primo (g) e deve poterli individuare in una stringa multilinea (s).
La riga 13 stampa la strina individuata dall'espressione contenuta nella seconda coppia di parentesi.
Questo programmino agisce sempre su un file HTML e ricerca e sostituisce un link con un altro.
#!/usr/bin/perl -pi.bak -00
s[
(
<\s*A
\s+HREF
\s*=\s*
(["'])
)
http://dominio\.it/dir/
(
(.*?)
\2
.*?>
)
][${1}http://www.dominio.it/dir2/$3]xsgi;
Se siete riusciti a seguire la tortuosa spiegazione del primo
programma, avete già capito gratis cosa fa questo.
Unica nota: l'opzione -p serve per eseguire il
programma su ogni riga
(paragrafo anche in questo caso, grazie all'opzione -00)
del file di
input e stampa automaticamente la riga letta dopo aver applicato la
ricerca e sostituzione, mentre l'opzione -i.bak serve
per far
eseguire il programma sul file di input, salvandone una copia con
estensione .bak.
Se chiamiamo il programma cambia_link, dando il comando:
$ cambia_link file.html
ritroviamo alla fine un file file.html in cui sono stati modificati i link e un file file.html.bak che è il file originale.
Questo programmino usa il modulo UserAgent per leggere una pagina da un server HTTP e stamparla sullo schermo. Niente di eccezionale così com'è, ma le estensioni sono lasciate alla vostra fantasia.
#!/usr/bin/perl
use LWP::UserAgent;
$agent = new LWP::UserAgent;
$request = new HTTP::Request('GET', 'http://gretux/');
$response = $agent->request($request);
print $response->content;
Per dimostrare l'attitudine che ha il Perl di poter scendere a livello di sistema, questo programmino sintetizza un pacchetto TCP/IP Out of Band e lo spedisce alla porta 139 di un computer:
#!/usr/bin/perl
use IO::Socket;
IO::Socket::INET->new(PeerAddr => "$ARGV[0]:" .
(defined $ARGV[1] ? $ARGV[1] : "139"),
Proto => "tcp")->send("bye",MSG_OOB);
Se chiamiamo questo programma crash, dando il comando:
$ crash host.dominio.it
verrà spedito un singolo pacchetto TCP/IP al computer
host.dominio.it, sulla porta 139 (default).
Aggiungendo un ulteriore parametro possiamo specificare la porta
verso la quale spedire il nostro pacchetto.
PGP Copertina Indice |