• Publicidad

Mejorar expresión regular de sustitución en varios ficheros

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Mejorar expresión regular de sustitución en varios ficheros

Notapor Santi » 2006-10-27 07:02 @334

Hola,

En un servidor están alojadas varias máquinas virtuales. Todas las madrugadas se ejecuta una tarea bastante pesada, en todas ellas a la misma hora, por lo que dejan al servidor medio frito:

Código: Seleccionar todo
# grep "statistics" /vz/root/*/var/spool/cron/root
/vz/root/1001/var/spool/cron/root:7     4       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1002/var/spool/cron/root:7     4       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1003/var/spool/cron/root:7     4       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1004/var/spool/cron/root:7     4       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1006/var/spool/cron/root:7     4       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1007/var/spool/cron/root:7     4       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1008/var/spool/cron/root:7     4       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1009/var/spool/cron/root:7     4       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1010/var/spool/cron/root:7     4       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1


Estoy preparando un script para dejar unos minutos entre cada tarea de modo que la carga se reparta a lo largo de la noche. Tengo una versión que ya funciona pero creo que todavía se podría hacer algo más elegante con vuestra experiencia, sobre todo en la parte de sustitución que queda algo chapucera.. ;-)

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl

my $incremento = 5;
my $m = 0;
my $h = 2;

foreach $fichero (glob("/vz/root/*/var/spool/cron/root")) {
        open(F, "$fichero") or die "ERROR al abrir $_";
        @lineas = <F>;
        close(F);

        open(F, ">$fichero") or die "ERROR al abrir $fichero en modo escritura";
        foreach $A (@lineas) {
                $A =~ s/^\d+\s+\d+\s+\*\s+\*\s+\*\s+\/usr\/local\/psa\/admin\/sbin\/statistics\s+>\/dev\/null\s+2>&1/$m\t$h\t*\t*\t*\t\/usr\/local\/psa\/admin\/sbin\/statistics >\/dev\/null 2>&1/g;
                print F $A;
        }
        $m += $incremento;
        if ($m > 59) {
                $m -= 60;
                $h++;
        }
        close(F);
}
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


La primera cosa que creo que se podría mejorar es la doble apertura del fichero, la primera en modo lectura para cargar el contenido y la segunda de escritura con los cambios en el cron. He estado probando con el switch "-i" que venía usando hasta ahora en los "one-liners" pero no me ha funcionado. En un post reciente de "explorer" he visto un caso similar en el que recomienda usar Sysadm::Install 'pie', la solución es muy buena pero no quisiera tener que instalar módulos adicionales...

La otra chapuza es la expresión regular utilizada para la sustitución... entiendo que todavía se puede reducir mucho más, pero no se cómo.. Las cadenas a cambiar siempre tienen este formato:

Código: Seleccionar todo
7 4 * * * /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1


Lo único a cambiar serían los dos primeros campos, los minutos y horas.. ¿Cómo mejorarías la expresión regular?

Tras ejecutar el script los crons quedan así:
Código: Seleccionar todo
/vz/root/1001/var/spool/cron/root:0     2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1002/var/spool/cron/root:5     2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1003/var/spool/cron/root:10    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1004/var/spool/cron/root:15    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1006/var/spool/cron/root:20    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1007/var/spool/cron/root:25    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1008/var/spool/cron/root:30    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1009/var/spool/cron/root:35    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1010/var/spool/cron/root:40    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1011/var/spool/cron/root:45    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1012/var/spool/cron/root:50    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1013/var/spool/cron/root:55    2       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1014/var/spool/cron/root:0     3       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1016/var/spool/cron/root:10    3       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1017/var/spool/cron/root:15    3       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1
/vz/root/1019/var/spool/cron/root:20    3       *       *       *       /usr/local/psa/admin/sbin/statistics >/dev/null 2>&1


A pesar de que el script ya hace su trabajo, creo que se puede mejorar bastante.. cualquier sugerencia es bienvenida :-)

Saludos,
Santi Saez
Santi
Perlero nuevo
Perlero nuevo
 
Mensajes: 13
Registrado: 2006-04-07 08:56 @414

Publicidad

Notapor explorer » 2006-10-27 08:03 @377

Yo lo veo bien...

En cuanto a la expresión regular, podría quedar reducida a esto:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$A =~ s/\d+\s+\d+(.+)/$m\t$h$1/;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Finalmente, quizás mejor el bucle interno así (una de las posibles):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  if ( $A =~ s/\d+\s+\d+(.+)/$m\t$h$1/ ) {
    $m += $incremento;
    $h += ( $min >= 60 ) ? 1 : 0;
    $m %= 60;
  }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor Santi » 2006-10-27 17:14 @760

Buenas,

Muchas gracias explorer como siempre por las sugerencias ;-)

La regex que pasas creo que no me serviría, se me paso comentar que dentro de los ficheros cron había bastantes más tareas y sólo quería cambiar una de ellas que era la que me estaba generando los problemas. La que tu pasas, está muy bien pensada.. pero me las cambiaría todas.

A pesar de que los ficheros "cron" no son muy grandes, no me acaba de convencer la idea de abrirlos, cargalos en memoria, procesarlos y grabarlos... Buscaba alguna solución tipo "pie", aunque entiendo que internamente se hace un proceso muy similar...

La última versión del script con las sugerencias aportadas por explorer es el siguiente:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
$task = "/usr/local/psa/admin/sbin/statistics";
$incremento = 5;
$h = 2;
$m = 0;

foreach $fichero (glob("/vz/root/*/var/spool/cron/root")) {
        open(F, "$fichero") or die "ERROR al abrir $_";
        @lineas = <F>;
        close F;

        open(F, ">$fichero") or die "ERROR al abrir $fichero en modo escritura";
        foreach $A (@lineas) {
                if ( $A =~ s/^\d+\s+\d+(.+)$task(.+)/$m\t$h$1$task$2/ ) {
                        $m += $incremento;
                        $h += ( $m >= 60 ) ? 1 : 0;
                        $m %= 60;
                }
                print F $A;
        }
        close F;
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Saludos,
Santi Saez
Santi
Perlero nuevo
Perlero nuevo
 
Mensajes: 13
Registrado: 2006-04-07 08:56 @414

Notapor explorer » 2006-10-27 19:22 @849

Bueno, ahora hay más información. Sólo hay una línea en el fichero cron que hay que modificar.

Vamos entonces a hacer la siguiente: leemos todo el fichero en una variable escalar. Si se puede realizar la sustitución, grabamos el nuevo cambio y aumentamos las variables de la hora; y seguimos con el siguiente $fichero.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
##### Leemos el $fichero
$cron = do { undef $/; open CRON,"<$fichero"; <CRON> };

if (
        $cron =~ s/^\s*\d+\s+\d+([ \t*]+$task.+)$/$m\t$h$1/m
   ) {
    ##### Grabación del cambio
    open  CRON, ">$fichero" or die "ERROR al intentar escribir en $fichero: $!\n";
    print CRON $cron;
    close CRON;

    ##### hubo un cambio. Aumentamos la hora para el siguiente $fichero
    $m += 5;
    $h += ( $m >= 60 ) ? 1 : 0;
    $m %= 60;
}

##### Siguiente $fichero
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Así, ya no te hace falta el bucle foreach $A ..., ni el array @lineas.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: Google [Bot] y 0 invitados

cron