[About] [Copertina] |
Articoli
Nessuna
notizia e' una buona notizia, recita un famoso detto inglese.
Spesso per noi che dipendiamo dalle News per avere la nostra dose
quotidiana di informazioni è difficile anche renderci conto che non
c'è proprio niente di interessante da leggere!
La quantità di notizie che ci arrivano tramite Internet
è spesso così
elevata da scoraggiare anche il più inpavido lettore.
Anche i filtri che i News readers ci mettono a disposizione sono
spesso troppo grossolani e corriamo il rischio di perdere qualche
informazione interessante o di averne così tante da non riuscire a
trovare quella giusta per noi.
La soluzione, da bravi Linux-men, è quella di fabbricarci i nostri
filtri su misura, interfacciandoci direttamente alle News, tramite il
NNTP (Net News Transfer Protocol), il protocollo utilizzato per il
trasferimento degli articoli tra news servers.
In questo articolo userò il linguaggio Perl e prenderò in esame un modulo aggiuntivo, che mette a disposizione del programmatore una classe che rende estremamente facile interfacciarsi al protocollo NNTP.
Supponendo che sulla vostra macchina sia installato il Perl, in una versione abbastanza recente (l'ultima versione al momento della scrittura di questo articolo è la 5.003), il componente che dovrete procurarvi è libnet-1.00.tar.gz .
L'installazione di questo modulo è molto semplice, come del resto quella di tutti i moduli aggiuntivi del Perl: basta scompattare il file, entrare nella directory creata e dare i seguenti comandi:
$ perl Makefile.PL
$ make
$ su -c 'make install'
A questo punto i moduli aggiuntivi e i relativi manuali saranno
installati nelle opportune directory (che il Perl desume dai dati
registrati nella sua installazione corrente).
Per leggere la pagina di manuale riguardante il modulo di interfaccia
al protocollo NNTP basta dare il comando:
$ man Net::NNTP
Ovviamente dovrete avere nel vostro $MANPATH o nel vostro /etc/manpath.config anche la directory dei manuali del Perl, che di solito è /usr/lib/perl5/man.
PRIMI PASSI: LA LISTA DEI NEWSGROUPS.
Chiunque abbia scritto un programma che si interfacci ad un protocollo
di rete ha sicuramente presente la quantità di istruzioni necessarie
per l'apertura di un socket su di una porta TCP/IP.
Tutto questo lavoro è ora incapsulato in una singola chiamata,
esattamente nella chiamata usata per l'inizializzazione di un oggetto
Net::NNTP.
Praticamente basta scrivere:
$c = new Net::NNTP('mio.news.server');
per aprire la connessione con la macchina specificata dalla stringa 'mio.news.server'.
Il primo programmino che possiamo scrivere ci può servire per leggere la lista dei newsgroups disponibili sul nostro News Server:
#!/usr/bin/perl
use Net::NNTP;
$ARGV[0] = 'localhost' if !defined $ARGV[0];
$c = new Net::NNTP($ARGV[0]);
$lista = $c->list();
foreach $newsgroup (keys %$lista){
print "$newsgroup\n";
}
Esaminiamolo riga per riga. La prima riga:
use Net::NNTP;
richiede al Perl di caricare il modulo che utilizzaremo. La riga:
$ARGV[0] = 'localhost' if !defined $ARGV[0];
Specifica un default ragionevole: se non sono presenti argomenti sulla
riga di comando (if !defined $ARGV[0]), allora assegna al primo
elemento dell'array in cui il Perl immagazzina la riga di comando, il
nome del server di default, in questo caso 'localhost': il nostro
server locale.
La riga:
$c = new Net::NNTP($ARGV[0]);
crea esplicitamente un oggetto della classe Net::NNTP. Dietro
questa riga, come abbiamo detto, si nasconde tutto il meccanismo di
connessione tramite socket alla porta NNTP del TCP/IP.
A questo punto non ci resta che inoltrare la richiesta della lista dei
newsgroups attivi sul server e stampare il risultato.
Tutto il colloquio tra il Server e il nostro client è sintetizzato
nella riga:
$lista = print $c->list();
che attiva la funzione membro 'list' dell'oggetto $c della classe
Net::NNTP.
La chiamata a questo metodo ritorna una reference (puntatore per i
fans del C) ad un array
associativo, le cui chiavi sono i nomi dei newsgroups presenti.
Le righe seguenti:
foreach $newsgroup (keys %$lista){
print "$newsgroup\n";
}
stampano i nomi dei newsgroups.
Il costrutto %$lista serve a dereferenziare l'intero l'array
associativo puntato dalla variabile $lista.
Se chiamiamo questo programma 'lista' e lo rendiamo eseguibile (chmod u+x lista), possiamo lanciarlo con il comando:
$ lista
e otterremo su standard output l'elenco dei newsgroup attivi sulla nostra macchina.
QUANTI ARTICOLI CI SONO NEL NEWSGROUP
Fin qui niente di particolarmente interessante, ma andiamo avanti. Scriviamo ora un programmino (si parla sempre di programmini: questo modulo ci consente di scrivere programmi complessi in pochissime righe!) per rilevare il numero del primo e dell'ultimo articolo presente in un newsgroup sul server specificato:
#!/usr/bin/perl
use Net::NNTP;
if(!defined $ARGV[0]){
print "Uso: $0 nome_gruppo [server]\n";
exit;
}
$ARGV[1] = 'localhost' if !defined $ARGV[1];
$c = new Net::NNTP($ARGV[1]);
print "(", join(",", $c->group($ARGV[0])), ")\n";
Esaminiamo anche questo programma riga per riga. Le prime righe sono simili a quello del programma 'lista', poi le quattro righe:
if(!defined $ARGV[0]){
print "Uso: $0 nome_gruppo [server]\n";
exit;
}
verificano che al programma sia stato passato almeno un parametro
sulla riga di comando: il nome del gruppo che vogliamo esaminare. In
caso contrario il programma esce dopo aver stampato la sintassi con
cui dovrebbe essere richiamato.
Le due righe successive:
$ARGV[1] = 'localhost' if !defined $ARGV[1];
$c = new Net::NNTP($ARGV[1]);
come abbiamo già visto definiscono un default per il nome del server da contattare e stabiliscono la connessione NNTP.
Vi sarete certamente resi conto che se l'utente per errore passa al programma il nome dell'host e dimentica il nome del newsgroup è probabile che si verifichi un errore, ma passatemi lo scarso controllo sui parametri, del resto questo è solo un programmino dimostrativo!
A questo punto, in un solo colpo, abbiamo la risposta:
print "(", join(",", $c->group($ARGV[0])), ")\n";
La funzione membro 'group' ritorna una lista di quattro elementi: il
numero di articoli presenti nel newsgroup, il
numero del primo e dell'ultimo articolo presenti e il nome nel newsgroup
specificato. La funzione join lega gli elementi della lista in una
unica stringa, separandoli con delle virgole.
Reso eseguibile questo programma e lanciato con il comando:
$ estremi_gruppo it.comp.linux
ottengo dal mio server una risposta del tipo:
(217,3511,3727,it.comp.linux)
GLI HEADERS DEGLI ARTICOLI.
Il passo successivo nella nostra esplorazione dei newsgroups è rappresentato da questo programma:
#!/usr/bin/perl
use Net::NNTP;
use Getopt::Std;
getopt('pu');
if(!defined $ARGV[0]){
print "Uso: $0 [-pprimo] [-uultimo] nome_gruppo [server]\n";
exit;
}
$ARGV[1] = 'localhost' if !defined $ARGV[1];
($c = new Net::NNTP($ARGV[1])) || die $!; # die on timeout
(($num_arts, $primo, $ultimo, $nome) = ($c->group($ARGV[0]))) || die $!;
$primo = $opt_p if defined $opt_p;
$ultimo = $opt_u if defined $opt_u;
$heads = $c->xhdr("Subject", $primo, $ultimo);
foreach $head (keys %$heads){
print "$head $heads->{$head}\n";
}
che serve per ottenere un elenco dei soli soggetti degli articoli presenti
in un newsgroup specificato. Esaminiamolo.
La prima righa:
use Net::NNTP;
come al solito serve per caricare il modulo che useremo. Le righe successive:
use Getopt::Std;
getopt('pu');
if(!defined $ARGV[0]){
print "Uso: $0 [-pprimo] [-uultimo] nome_gruppo [server]\n";
exit;
}
servono per caricare e usare una libreria compresa nella distribuzione
standard del Perl, che serve per esaminare la riga di comando alla
ricerca di opzioni.
Le opzioni che cerchiamo sono '-p' e '-u', entrambe seguite da un
valore numerico, il cui scopo è definire il numero del primo e
dell'ultimo articolo da esaminare.
Se la linea di comando è vuota, usciamo dal programma, proponendo un messaggio chiarificatore sull'uso.
La riga successiva:
$ARGV[1] = 'localhost' if !defined $ARGV[1];
definisce un default ragionevole per il nome del server da contattare.
La riga:
($c = new Net::NNTP($ARGV[1])) || die $!; # die on timeout
avvia la connessione con il server, questa volta controllando anche il
risultato dell'operazione e uscendo in caso di errore o timeout.
Le righe successive:
(($num_arts, $primo, $ultimo, $nome) = ($c->group($ARGV[0]))) || die $!;
$primo = $opt_p if defined $opt_p;
$ultimo = $opt_u if defined $opt_u;
leggono il numero del primo e dell'ultimo articolo presenti nel
newsgroup specificato, per utilizzarli come defaults, in caso l'utente
non abbia specificato il numero del primo e dell'ultimo articolo da
esaminare.
Alla fine le righe:
$heads = $c->xhdr("Subject", $primo, $ultimo);
foreach $head (keys %$heads){
print "$head $heads->{$head}\n";
}
stampano tutti i soggetti degli articoli specificati.
La funzione membro xdhr() torna una reference ad un array
associativo, le cui chiavi sono i numeri degli articoli e i cui valori
sono l'header richiesto per l'articolo.
Il costrutto $heads->{$head} serve per dereferenziare
un elemento dell'array a partire dal suo reference.
Reso eseguibile questo programma e lanciatolo con il comando:
$ headers_articoli -p34000 -u34010 comp.databases.oracle
ottengo dal mio server la risposta:
34000 Cursor: does the job but doesn't come back
34001 Re: Database writing architecture
34002 Help w/ D-2000 connection
34003 Re: Cursor: does the job but doesn't come back
34004 Re: Oracle 7.3 features
34005 Applications Programmers-Twin Cities-RTP NC
34006 Re: Java-Oracle are there any classes available?
34007 Re: PO7 upgrade to Win95.
34008 US-IL-ORACLE/ORACLE FINANCIALS DEVELOPERS & DBA'S
34009 Excellent Opportunities-RTP NCWash DCMinneapolis
34010 BIND variables?
UNA RICERCA!
Ma tutto ciò può essere fatto da un qualsiasi news reader!
Le cose cominciano a diventare utili se modifichiamo una sola riga del
nostro programma in modo che stampi il numero di tutti gli
articoli il cui soggetto contiene una certa stringa:
#!/usr/bin/perl
use Getopt::Std;
getopt('pu');
use Net::NNTP;
if(!defined $ARGV[1]){
print "Uso: $0 [-pprimo] [-uultimo] nome_gruppo stringa [server]\n";
exit;
}
$ARGV[2] = 'localhost' if !defined $ARGV[2];
($c = new Net::NNTP($ARGV[2])) || die $!; # die on timeout
(($num_arts, $primo, $ultimo, $nome) = ($c->group($ARGV[0]))) || die $!;
$primo = $opt_p if defined $opt_p;
$ultimo = $opt_u if defined $opt_u;
$heads = $c->xpat("Subject", $ARGV[1], $primo, $ultimo);
foreach $head (keys %$heads){
print "$head $heads->{$head}\n";
}
Il programma cerca_articoli ora, lanciato così:
$ cerca_articoli comp.lang.perl.misc '*UNIX*'
genera sulla mia macchina questo output:
18263 Simple UNIX program scheduling...
18264 UNIX Symlinks and -i.bak
18266 Re: Simple UNIX program scheduling...
18273 Re: Simple UNIX program scheduling...
Ci sono due cose da notare: la prima è che il pattern viene
interpretato dal server NNTP, quindi si usano dei metacaratteri
diversi da quelli delle regular expression del Perl, più simili
ai metacaratteri della shell.
La seconda cosa è che il pattern deve matchare tutta la
stringa cercata: nel caso si compia una ricerca sul
"Subject" dell'articolo, il pattern deve corrispondere
all'intero soggetto, non ad una parte di esso.
Questo spiega gli asterischi prima e dopo la stringa da cercare.
Ovviamente usando lo stesso metodo posso cercare una stringa all'interno di qualsiasi altra riga dell'header, specificandone il nome al posto del "Subject", così, per cercare gli articoli scritti dal signor Rossi:
$heads = $c->xpat("From", "*Rossi*", $primo, $ultimo);
A questo punto abbiamo già il modo di selezionare un po' gli articoli prima della lettura, ma vogliamo raffinare ancora il nostro programmino. Facciamo in modo che la nostra creatura possa leggere anche il testo dell'articolo e che possa "decidere" cosa ci interessa.
Qualche altra riga di codice e otteniamo:
#!/usr/bin/perl
use Getopt::Std;
getopt('pu');
use Net::NNTP;
if(!defined $ARGV[1]){
print "Uso: $0 [-pprimo] [-uultimo] nome_gruppo stringa_header stringa_body [server]\n";
exit;
}
$ARGV[3] = 'localhost' if !defined $ARGV[3];
($c = new Net::NNTP($ARGV[3])) || die $!; # die on timeout
(($num_arts, $primo, $ultimo, $nome) = ($c->group($ARGV[0]))) || die $!;
$primo = $opt_p if defined $opt_p;
$ultimo = $opt_u if defined $opt_u;
$heads = $c->xpat("Subject", $ARGV[1], $primo, $ultimo);
foreach $numero (keys %$heads){
$art = $c->article($numero);
$testo = join "", @$art;
if($testo =~ /$ARGV[2]/o){
print "$testo\n\n";
}
}
Qualche commento: il numero dei parametri
richiesti dal programma è cresciuto: oltre alla stringa da cercare
nell'header ora ci aspettiamo dall'utente anche una stringa da cercare
all'interno del testo dell'articolo.
Dopo la chiamata alla funzione membro xpath()
scorriamo tutte le righe dell'array, leggendo il testo dell'articolo
mediante la funzione membro article().
Questa funzione torna una reference ad un array che contiene tutte le
righe dell'articolo (variabile $art).
Uniamo tutte le righe dell'array con una join per formare
un'unica variabile, all'interno della quale cercheremo poi il secondo
testo specificato sulla riga di comando.
Invocando il programma in questo modo:
$ cerca -p18254 -u18280 comp.lang.perl.misc '*UNIX*' 'crontab'
ottengo questo l'output:
Path: nanux!comune.bologna.it!sirio.cineca.it!serra.unipi.it!swidir.switch.ch!in2p3.fr!oleane!tank.news.pipex.net!pipex!news.mathworks.com!newsfeed.internetmci.com!info.ucla.edu!ihnp4.ucsd.edu!munnari.OZ.AU!mel.dit.csiro.au!carlton.acci.COM.AU!gavin
From: gavin@acci.COM.AU (Gavin Cameron)
Newsgroups: comp.lang.perl.misc,comp.unix.programmer
Subject: Re: Simple UNIX program scheduling...
Date: 11 Apr 96 00:35:28 GMT
Organization: Australian Computing and Communications Institute
Lines: 23
Message-ID: <gavin.829182928@carlton.acci.COM.AU>
References: <4khh0i$fv2@solaris.cc.vt.edu>
NNTP-Posting-Host: tawonga.acci.com.au
X-Newsreader: NN version 6.5.0 #6 (NOV)
Xref: nanux comp.lang.perl.misc:18273 comp.unix.programmer:5376
Use crontab, see the crontab(5) man page for all the details you'll ever
need to know.
Gavin
sms@magenta.com (SMS/Christian Fowler) writes:
>I have a simple program, written with PERL, I want to schedule execution
>for. What is the simplest way to do this? Should I have the program
>reschedule itself, every time it runs?
>I simply want it to run, say every monday morning at 7 am.
>Thanks for any info...
>--
> =-=
>=-=+=-= Sound Machine Sound - The Music Makers Net Directory
>=-=%=-= Christian Fowler - sHAPE FACTOR MOMENt
>=-=+=-= sms@magenta.com http://magenta.com/~sms/
> =-=
Purtroppo questa volta le stringhe che esprimono i pattern da cercare
seguono due standard diversi: la prima stringa viene interpretata dal
server NNTP e segue uno standard pseudo-shell, mentre la
seconda stringa, interpretata dal Perl può essere una regular
expression.
Spiacente per la confusione, ma a servizi diversi corrispondono
formalismi diversi... fino a quando qualcuno non scriverà un
server NNTP in Perl (oops!).
È comunque
un buon risultato per un programma di venticinque righe, comprese righe
vuote e commenti!
C'è da dire che il programmino non ha molte pretese,
che può essere migliorato, ma è sufficiente a dimostrare
cosa si può fare con questo
modulo e spero che possa fornirvi degli spunti per creare qualcosa di
più complesso... interfacce News-Web? risponditori intelligenti?
Si potrebbe usare il modulo che si interfaccia al protocollo SMTP per
spedire mails di biasimo automatiche a chi scrive sciocchezze... ma
questa è un'altra storia e va raccontata un'altra volta.
Nel frattempo vi auguro felice hacking sulla vostra Linux box e se realizzate qualcosa di interessante non mancate di farmelo sapere e magari scrivete un'articolo per il Pluto Journal.
A rileggerci alla prossima. Nel frattempo per dubbi, congratulazioni, correzioni, insulti & altro scrivete a Nando Santagata.
Il modulo citato si può trovare in uno dei siti del CPAN (Comprehensive Perl Archive Network) di cui vi elenco i siti in Europa:
Austria
ftp://ftp.tuwien.ac.at/pub/languages/perl/CPAN/
Belgium
ftp://ftp.kulnet.kuleuven.ac.be/pub/mirror/CPAN/
Czech Republic
ftp://sunsite.mff.cuni.cz/MIRRORS/ftp.funet.fi/pub/languages/perl/CPAN/
Denmark
ftp://sunsite.auc.dk/pub/languages/perl/CPAN/
Finland
ftp://ftp.funet.fi/pub/languages/perl/CPAN/
France
ftp://ftp.ibp.fr/pub/perl/CPAN/
ftp://ftp.pasteur.fr/pub/computing/unix/perl/CPAN/
Germany
ftp://ftp.leo.org/pub/comp/programming/languages/perl/CPAN/
ftp://ftp.rz.ruhr-uni-bochum.de/pub/CPAN/
Greece
ftp://ftp.ntua.gr/pub/lang/perl/
Hungary
ftp://ftp.kfki.hu/pub/packages/perl/CPAN/
Poland
ftp://ftp.pk.edu.pl/pub/lang/perl/CPAN/
ftp://sunsite.icm.edu.pl/pub/CPAN/
Portugal
ftp://ftp.ci.uminho.pt/pub/lang/perl/
Slovenia
ftp://ftp.arnes.si/software/perl/CPAN/
Spain
ftp://ftp.etse.urv.es/pub/mirror/perl/
ftp://ftp.rediris.es/mirror/CPAN/
Sweden
ftp://ftp.sunet.se/pub/lang/perl/CPAN/
Switzerland
ftp://ftp.switch.ch/mirror/CPAN/
the Netherlands
ftp://ftp.cs.ruu.nl/pub/PERL/CPAN/
UK
ftp://ftp.demon.co.uk/pub/mirrors/perl/CPAN/
ftp://sunsite.doc.ic.ac.uk/packages/CPAN/
ftp://unix.hensa.ac.uk/mirrors/perl-CPAN/
L'Italia non ha un sito CPAN (qualche volontario?).
Il modulo in questione si trova, a partire dalla directory del CPAN indicata per ogni server, in:
modules/by-module/Net
[About] [Copertina] |