Echando código: Borrón y cuenta nueva

Estoy trabajando (de puro ocio dominical) en un pequeño script que se baja los datos de un par de sitios en Internet, los cuales (grátis) te hacen la correspondencia entre direcciones IP y un pais (con latitud y longitud). Así que sin pensar mucho me senté a escribir y al final terminé haciendo un script en Bash que hacia todo, con ayuda de muchas herramientas que ya vienen instaladas bajo Linux:
1:#!/bin/bashY fué aqui en donde me pegó de frente: ¿Que hacia yo llamando a Perl varias veces dentro de el código, para acomodar al archivo antes de cargarlo? Lo peor fué que me di cuenta justo cuando un caso especial para procesar ciertas lineas de uno de los archivos fué requerido (un "perl one liner") simplemente no podía.
2:
3:# This script does the following tasks for the Yahoo log mapping:
4:# - Download the data from several sources on the Internet
5:# - Performs basic cleanup to allow the data import
6:# - Imports the data
7:
8:declare -r SCRIPT="${0##*/}"
9:declare -r DATA="$HOME/data"
10:declare -r GEO_FILE="country.db"
11:declare -r GEO_URL="http://ip.ludost.net/raw/${GEO_FILE}.gz"
12:declare -r ASLATLONG_FILE="aslatlong.txt"
13:declare -r ASLATLONG_URL="http://netgeo.caida.org/$ASLATLONG_FILE"
14:declare -r DATABASE="kodeg2_kodegeek"
15:declare -r USER="kodeg2_rw"
16:
17:if [ ! -d "$DATA" ]; then
18: /bin/mkdir "$DATA"
19: if [ $? != 0 ]; then
20: printf "Unable to create %s\n" "$DATA"
21: fi
22:fi
23:
24:function cleanup {
25: test -f "$DATA/$GEO_FILE" && /bin/rm -f "$DATA/$GEO_FILE"
26: test -f "$DATA/$ASLATLONG_FILE" && /bin/rm -f "$DATA/$ASLATLONG_FILE"
27:}
28:#declare trap cleanup exit
29:
30:# Download and unpack the files
31:cleanup
32:
33:cd $DATA
34:if [ $? != 0 ]; then
35: exit 192
36:fi
37:
38:/usr/bin/wget -nv $ASLATLONG_URL
39:if [ $? != 0 ]; then
40: exit 192
41:else
42: # Remove the comments from the original file
43: egrep -v "^#" $ASLATLONG_FILE > ${ASLATLONG_FILE}.$$ &&
44: mv ${ASLATLONG_FILE}.$$ $ASLATLONG_FILE &&
45: /bin/chmod a+r $DATA/$ASLATLONG_FILE
46: if [ $? != 0 ]; then
47: exit 193
48: fi
49:fi
50:
51:# A IP address can be mapped up to 6 countries.
52:# The file is not properly escaped, fix the first delimiters
53:/usr/bin/wget -nv $GEO_URL &&
54:/usr/bin/gunzip -f "$DATA/${GEO_FILE}.gz" &&
55:perl -p -i -e's# #\t#' "$DATA/${GEO_FILE}.gz" &&
56:perl -p -i -e's# #\t#' "$DATA/${GEO_FILE}.gz"
57:/bin/chmod a+r $DATA/${GEO_FILE}
58:if [ $? != 0 ]; then
59: exit 192
60:fi
61:
62:# Import the files into the database
63:cat <<EOF > $DATA/${SCRIPT}.sql
64:truncate table geo_ip;
65:\\copy geo_ip FROM '$DATA/$GEO_FILE' DELIMITERS ' '
66:truncate table aslatlong;
67:\\copy aslatlong FROM '$DATA/$ASLATLONG_FILE' DELIMITERS '\t'
68:EOF
69:if [ $? != 0 ]; then
70: exit 192
71:fi
72:
73:#psql $DATABASE -U $USER < $DATA/${SCRIPT}.sql
74:if [ $? != 0 ]; then
75: exit 192
76:fi
OK, sientate un poco. ¿Lo hago en Java? No, el formato puede cambiar y no me quiero ver compilando el parser, además de que no quiero meterme con las clases java.net.URL ni lidiar com compresión en streams. En pocas palabras quería un hack, algo rápido para poder dedicarme a la parte de el problema que realmente me interesa. Perl es fácil de usar, muy bueno manipulando texto así que ¿porqué no?:
1:#!/usr/bin/perlSi, son más líneas de código, pero me tomó la misma cantidad de tiempo hacer esta versión y a diferencia de el anterior este si hace lo que yo quiero. La moraleja de la historia es que siempre es bueno sentarse a pensar aunque sea un poquito antes de ponerse a echar código en el computador :D
2:
3:use strict;
4:use LWP::UserAgent;
5:use threads;
6:use threads::shared;
7:
8:use constant DATA => "$ENV{HOME}/data";
9:use constant GEO_FILE => "country.db";
10:use constant GEO_URL => "http://ip.ludost.net/raw/" . GEO_FILE . ".gz";
11:use constant GEO_PARSED_FILE => DATA . "/country.db";
12:use constant ASLATLONG_FILE => "aslatlong.txt";
13:use constant ASLATLONG_URL => "http://netgeo.caida.org/" . ASLATLONG_FILE;
14:use constant ASLATLONG_PARSED_FILE => DATA . "/aslatlong_parsed.txt";
15:
16:if ( ! -d DATA ) {
17: printf "%s\n", DATA;
18: mkdir DATA;
19:}
20:
21:my (@downloadThreads, @parseThreads);
22:
23:# Set the callbacks. Order is important
24:my @parseCallbacks = (\&parseGeoFile, \&parseASFile);
25:# Set the files. Order is important
26:my @files = (DATA . "/" . GEO_FILE . ".gz", DATA . "/" . ASLATLONG_FILE);
27:# Set the URL. Order is important
28:my @url = (GEO_URL, ASLATLONG_URL);
29:
30:for (my $idx=0; $idx < scalar(@url); $idx++) {
31: $downloadThreads[$idx] = threads->new(
32: \&getURL,
33: $url[$idx],
34: $files[$idx]);
35:}
36:
37:for (my $idx=0; $idx < scalar(@downloadThreads); $idx++) {
38: my $result = $downloadThreads[$idx]->join();
39: if (! (($result eq "304 Not Modified") || ($result eq "200 OK")) ) {
40: printf "[ERROR]: Downloading one of the files, %s, %s",
41: $downloadThreads[$idx]->tid(), $result;
42: exit 192;
43: }
44: $parseThreads[$idx] = threads->new(\&{$parseCallbacks[$idx]}, $files[$idx]);
45:}
46:
47:for (my $idx=0; $idx < scalar(@parseThreads); $idx++) {
48: my $result = $parseThreads[$idx]->join();
49:}
50:
51:
52:# Download the file from the desired URL
53:sub getURL {
54: my ($url, $file) = @_;
55: printf "[INFO]: Downloading %s -> %s\n", $url, $file;
56: my $ua = LWP::UserAgent->new(
57: agent => "kodeGeek/GeoData 1.0",
58: from => 'josevnz@kodegeek.com'
59: );
60: my $response = $ua->mirror($url, $file);
61: return $response->status_line;
62:}
63:
64:# Parse the GeoFile
65:sub parseGeoFile {
66: my $file = $_[0];
67: printf "[INFO]: Parsing GeoFile %s -> %s\n",
68: $file, GEO_PARSED_FILE;
69: open(INPUT_FILE, "/bin/zcat $file |") || die "$!";
# Not portable, but who cares!
70: open(PARSED_FILE, "> " . GEO_PARSED_FILE) || die "$!";
71: while(<INPUT_FILE>) {
72: chomp($_);
73: my @tokens = split(' ', $_);
74: # Fix the separator and make the countries
appear as a single token
75: printf PARSED_FILE "%s\t%s\t%s\n",
76: shift @tokens, shift @tokens, join(' ', @tokens);
77: }
78: close(INPUT_FILE);
79: close(PARSED_FILE);
80:}
81:
82:# Parse the AS file
83:sub parseASFile {
84: my $file = $_[0];
85: printf "[INFO]: Parsing ASFile %s ->%s\n",
86: $file, ASLATLONG_PARSED_FILE;
87: open(PARSED_FILE, "> " . ASLATLONG_PARSED_FILE) || die "$!";
88: open(INPUT_FILE, "$file") || die "$!";
89: while(<INPUT_FILE>) {
90: next if $_ =~ /^#/;
91: chomp($_);
92: $_ =~ s#^(\d+) - (\d+)#${1}-${2}#;
93: my @tokens = split('\t', $_);
94: # Generate the extra tokens. This will increase
the size of the file at least 3x.
95: if ($tokens[0] =~ /(\d+)\-(\d+)/) {
96: my $start=$1;
97: my $end=$2;
98: shift @tokens; # Discard the first token,
will be generated.
99: for ($start=$1; $start <= $end; $start++) {
100: printf PARSED_FILE "%s\t%s\n",
101: $start, join("\t", @tokens);
102: }
103: } else {
104: printf PARSED_FILE "%s\n",
105: join("\t", @tokens);
106: }
107: }
108: close(INPUT_FILE);
109: close(PARSED_FILE);
110:}
111:
112:__END__
113:=head1 NAME
114:import_geo_data.pl - Retrieve the Geographic data related with IP
115:
116:=head1 DESCRIPTION
117:
118:The script downloads and prepares for import data
from several free sources.
119:
120:=head1 LICENSE
121:
122:GPL
123:
124:=head1 AUTHOR
125:
126:Jose Vicente Nunez Zuleta (josevnz@kodegeek.com)
127:
128:=cut
Dentro de poco pondré el código por separado, pero si tiene prisa puede bajarselo de CVS.
Buscar en Technorati: perl



5 Comentarios:
Por otro lado, uno nunca se espera que el proveedor de Internet en donde vamos a correr el script tenga una versión the Perl sin soporte the Threads:
/home/kodeg2/bin/import_geo_data.pl line 5:
This Perl hasn't been configured and built properly for the threads
module to work. (The 'useithreads' configuration option hasn't been
used.)
Having threads support requires all of Perl and all of the XS modules
in
the Perl installation to be rebuilt, it is not just a question of
adding
the threads module. (In other words, threaded and non-threaded Perls
are binary incompatible.)
If you want to the use the threads module, please contact the people
who built your Perl.
Cannot continue, aborting.
BEGIN failed--compilation aborted at
/usr/lib/perl5/5.8.6/i686-linux/threads.pm line 28.
Compilation failed in require at /home/kodeg2/bin/import_geo_data.pl
line 5.
BEGIN failed--compilation aborted at
/home/kodeg2/bin/import_geo_data.pl line 5.
jajajaja...
(siempre es bueno tomarse un poquito de tiempo para leer la letra pequeña!)
Fascinante, de todos modos.
Como ya he dicho antes, no sé nada de programar, pero es muy interesante verte resolver estos problemas y compartirlo con nosotros.
Es como ir leyendo una novela, donde entran en la parte técnica y el autor te lanza la parrafada, pero igual la entiendes y se te hace amena de leer.
Ojalá el script sea útil a alguien.
¡Que defecada!. El código en Perl corre en la mitad de tiempo, hace mas cosas y es más fácil de mantener. Pero la compañia que hospeda el blog no soporta threads en Perl, así que creo que al final me va a tocar hacer el asunto en Java.
Supongo que la próxima vez deberé revizar que la plataforma que decida utilizar sea factible (por ejempo, yo se que esta compañia no soporta Ruby ni Jython).
Arrrrggg, que ladilla :(
Mis temores fueron confirmados, lo que no me convenven son las razones. En fin:...
Reply:
Hello,
Unfortunately we do not support Perl with threads. Enabling threads would, as
the message states, require a complete recompile of the Perl installation.
Because customers are able to install custom modules, including XS code, on
their accounts, re-compiling with threads support would require these modules
be re-compiled.
Additionally, there are different forms of Perl thread support and they cannot
be used simultaneously. So if someone were to request 5.005 version support
and somone else requested the new threading support, we would be unable to
provide the requested support to one of the customers.
Finally, because threads in Perl are relatively new, many scripts and exsting
modules, even those modules in the core installation, are not thread safe and
could potentially cause issues in the shared environment.
In order to have perl threads, you would need a dedicated server where you
could recompile Perl with any additional options you desired.
--
Kind Regards,
Ya nos pasó una vez en la empresa algo como eso, pero creo que fué con Macromedia Coldfusion. El proveedor digo algo similar: ni modo, no lo montamos así desde un principio, y ahora sería muy difícil hacerlo.
La cag@#$@
Publicar un comentario en la entrada
Enlaces a este articulo:
Crear un enlace
<< Regresar