Perl en Español

  1. Home
  2. Tutoriales
  3. Foro
  4. Artículos
  5. Donativos
  6. Publicidad
 

Buscar y sustituir cadenas en un fichero

 
Publicar nuevo tema   Responder al tema    Foros de discusión -> Básico
Mensaje Jue Oct 09, 2008 5:14 pm
rucar
Perlero Nuevo
Perlero Nuevo
Registrado: 09 Oct 2008
Mensajes: 5
Buscar y sustituir cadenas en un fichero Responder citando

Hola a todos, soy nuevo en este foro y sobre todo en Perl.
Estoy leyendo a marchas forzadas porque me corre mucha prisa realizar un script, pero voy a trompicones y me atraganto.

Estoy intentando modificar un fichero ldif para la entrada masiva de usuarios en LDAP. El contenido del mismo podría ser como el siguiente

Código:
dn: uid=jperez,ou=People,dc=ejemplo,dc=com
uid: jperez
objectclass: account
objectclass: top
uidnumber: 512
gidnumber: 300
homedirectory: /home/jperez
userpassword: jperez

dn: uid=domingo,ou=People,dc=ejemplo,dc=com
uid: domingo
objectclass: account
objectclass: top
uidnumber: 512
gidnumber: 300
homedirectory: /home/domingo
userpassword: domingo

dn: uid=pepe,ou=People,dc=ejemplo,dc=com
uid: pepe
objectclass: account
objectclass: top
uidnumber: 512
gidnumber: 300
homedirectory: /home/pepe
userpassword: pepe


Tiene varios bloques iguales y tengo que encontrar solamente las líneas que contiene la cadena "userpassword" y después recoger el nombre que viene a continuación de los dos puntos (:) y pasarle otra función que me sustituya dicho nombre por el mismo codificado, algo que he encontrado como lo siguiente:

Perl:
#!/usr/bin/perl
$claro=$ARGV[0];
chomp $claro;
$salt = join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64];
printf "%s\n", crypt("$claro", "$salt");


Sé que con grep() me devuelve las líneas que contiene dicha cadena.

¿Me trata cada una de ellas con un "foreach" para la sustitución individual de cada línea y nombre? ¿Cómo separo el trozo de cadena que me hace falta? ¿Cómo abro el fichero?

¿Podéis ayudarme con el código? Muchas gracias de antemano.
Mensaje Jue Oct 09, 2008 9:11 pm
explorer
Moderador
Moderador
Registrado: 24 Jul 2005
Mensajes: 4082
Ubicación: Valladolid, España
Responder citando

Bienvenido a los foros de Perl en Español, rucar.

Esta es una forma de resolver el tema:
Perl:
        1 #!/usr/bin/perl
        2 use strict;
        3 use warnings;
        4 use diagnostics;
        5
        6 my @salto = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
        7
        8 my $linea;
        9 my $usuario;
       10 my $salto;
       11
       12 open(FICHERO,"<ldif.txt") or die "ERROR: No puedo abrirlo: $!\n";
       13
       14 while  ($linea   = <FICHERO>) {
       15     if ($linea   =~ m/^userpassword: (\w+)/) {
       16         $salto   = join('', @salto[rand @salto, rand @salto]);
       17         $usuario = crypt($1, $salto);
       18         print "userpassword: $usuario\n";
       19     }
       20     else {
       21         print $linea;
       22     }
       23 }
       24
       25 close(FICHERO);
       26

En resumen:
* De las líneas 2 a 4 ajustamos unas opciones que nos ayudarán a identificar errores de programación
* La línea 6 declara y define los caracteres de salto, del nombre de usuario, codificado
* De la 8 a la 10, declaramos unas variables
* En la línea 12 abrimos el fichero de entrada
* El bucle de la línea 14 a la 23 se repite por cada línea del fichero de entrada
* 15: Si la $linea empieza (^) por 'userpassword: ' seguido de una palabra (conjunto de una o más (+) caracteres alfanuméricos (\w)), entonces guarda esa palabra en la variable $1 (por efecto de los paréntesis de captura)
* En la 16 sacamos el $salto (dos caracteres al azar extraídos desde @salto)
* En la 17 codificamos el nombre del usuario (que estaba en $1) con el $salto
* y en la 18 sacamos la nueva línea modificada
* En la 21, sacamos la línea tal cual en caso de no ser la que nos interesa modificar
* 25: cerramos y nos vamos

Si queremos guardar la salida en un fichero debemos hacer otro open(), otro close() y modificar los print para redirigir la salida hacia el fichero de salida.

Y ya está. Pero se puede hacer de otras diez formas distintas...

Por ejemplo, esta otra:
Perl:
#!/usr/bin/perl -p
my @salto = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
s/^(userpassword: )(\w+)/$1 . crypt($2, join('', @salto[rand @salto, rand @salto]))/e;
Con
bash:
./ldif.pl ldif.txt
sale:
Código:
dn: uid=jperez,ou=People,dc=ejemplo,dc=com
uid: jperez
objectclass: account
objectclass: top
uidnumber: 512
gidnumber: 300
homedirectory: /home/jperez
userpassword: uF6YatSuvV/ls

dn: uid=domingo,ou=People,dc=ejemplo,dc=com
uid: domingo
objectclass: account
objectclass: top
uidnumber: 512
gidnumber: 300
homedirectory: /home/domingo
userpassword: 8KSR4bWoW0HHw

dn: uid=pepe,ou=People,dc=ejemplo,dc=com
uid: pepe
objectclass: account
objectclass: top
uidnumber: 512
gidnumber: 300
homedirectory: /home/pepe
userpassword: /pi6xlVZDBNKg
Mensaje Vie Oct 10, 2008 5:30 am
rucar
Perlero Nuevo
Perlero Nuevo
Registrado: 09 Oct 2008
Mensajes: 5
Responder citando

Muchas gracias, explorer,

Me sirve de lujo, era lo que estaba buscando. He elegido la segunda opción que me planteas, lanzando el perl sobre el fichero de texto.

Muchas gracias.
Mensaje Vie Oct 10, 2008 7:12 am
explorer
Moderador
Moderador
Registrado: 24 Jul 2005
Mensajes: 4082
Ubicación: Valladolid, España
Responder citando

Bien. Ahora tienes como deberes el que expliques aquí cómo funciona la segunda solución y aportar una tercera.

Pero sin prisas...
Mensaje Lun Oct 13, 2008 5:11 pm
rucar
Perlero Nuevo
Perlero Nuevo
Registrado: 09 Oct 2008
Mensajes: 5
Responder citando

Perl:
#!/usr/bin/perl -p
my @salto = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
s/^(userpassword: )(\w+)/$1 . crypt($2, join('', @salto[rand @salto, rand @salto]))/e;


Te paso a responder, pues me ha picado el tema y he echado un vistazo a algunos tutoriales, aunque hay cosas que todavía no comprendo muy bien.

La primera línea declara y asigna valores a una variable tipo array "my @salto", con distintos elementos como punto (.), barra (/), y rango de números y letras mayúsculas y minúsculas...

En la segunda línea sustituye (s) lo que hay entre los delimitadores ///, es decir, la línea que comienza (^) con "userpassword:" seguido de uno o más caracteres alfabéticos "\w+" (de esto no estoy muy seguro), y lo sustituye por la concatenación del contenido de la variable $1 que fue asignado por los dos primeros "()", más el encriptado del contenido de la variable $2, asignado por los segundos "()".

La función crypt() usa como parámetros crypt(contraseña, clave). La contraseña es la cadena pasada, en este caso $2. Y la clave es la unión por la función join() de dos cadenas.

En esto último es donde me pierdo y no sé exactamente lo que hace la función random() con el array: @salto[rand @salto, rand @salto].

Tampoco sé lo que hace el comando o argumento /e.

¿Podrías explicármelo?

Muchas gracias.
Mensaje Lun Oct 13, 2008 5:38 pm
rucar
Perlero Nuevo
Perlero Nuevo
Registrado: 09 Oct 2008
Mensajes: 5
Responder citando

Sigo teniendo algún problemilla con script.

Si la cadena en lugar de ser: "userpassword: domingo", es: "userpassword: domingo.lopez", solo me encripta la parte que está antes del punto y me respeta lo otro.

Además el fichero original me lo deja sin cambiar, a pesar de ejecutar correctamente el código.

También se me olvido preguntar antes que significa el argumento "-p" de la llamada al compilador "#!/usr/bin/perl -p"
Mensaje Lun Oct 13, 2008 5:42 pm
explorer
Moderador
Moderador
Registrado: 24 Jul 2005
Mensajes: 4082
Ubicación: Valladolid, España
Responder citando

/e le indica a la expresión regular que la segunda parte es un trozo de código Perl que debe ejecutar, y su salida será la que sustituya a la primera parte de la exp. reg.

En cuanto a @salto[rand @salto, rand @salto].

* @salto es un array con varios elementos
* rand @salto obliga a evaluar @salto en contexto escalar, por lo que obtenemos el número de elementos de @salto y ese será el parámetro que le pasamos a rand(). Entonces, rand() nos devolverá un valor entero aleatorio entre 0 (inclusive) y el número de elementos de @salto (excluido)
* Eso lo hacemos dos veces, para obtener dos números
* Usamos esos dos números como índices para extraer dos letras del array @salto. Como son dos valores, estamos extrayendo una lista de elementos. Por eso ponemos una '@' delante
* Las dos letras extraídas son unidas con la ayuda de join().
Mensaje Lun Oct 13, 2008 6:01 pm
explorer
Moderador
Moderador
Registrado: 24 Jul 2005
Mensajes: 4082
Ubicación: Valladolid, España
Responder citando

rucar escribió:
Sigo teniendo algún problemilla con script.

Si la cadena en lugar de ser: "userpassword: domingo", es: "userpassword: domingo.lopez", solo me encripta la parte que está antes del punto y me respeta lo otro.


Cambia (\w+) por (.+). El \w solo es para caracteres alfanuméricos, y el '.' no lo es. Así que ponemos un comodín genérico, y listo. Nos quedamos con todo el resto de la línea.

rucar escribió:
También se me olvido preguntar antes que significa el argumento "-p" de la llamada al compilador "#!/usr/bin/perl -p"

Quiere decir que tiene que abrir el fichero que le indiquemos y hacer un bucle por todas las filas. Por cada fila, aplicará el programa que hemos escrito. Y al final, sacará la linea por la salida estándar. Algo así:

Perl:
open(FICHERO, "<$ARGV[0]");
while (<FICHERO>) {
    # ... nuestro programa ...

    print;
}
close FICHERO;


rucar escribió:
Además el fichero original me lo deja sin cambiar, a pesar de ejecutar correctamente el código.
Claro, porque no nos has dicho qué hacer con los cambios... así que de momento solo salen por la salida estándar.

Si quieres que se haga el cambio in-situ, en el propio fichero, hay que agregar la opción -i. Quedaría:
Perl:
#!/usr/bin/perl -pi
my @salto = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
s/^(userpassword:\s+)(.+)/$1 . crypt($2, join('', @salto[rand @salto, rand @salto]))/e;
Mensaje Mar Oct 14, 2008 3:21 am
rucar
Perlero Nuevo
Perlero Nuevo
Registrado: 09 Oct 2008
Mensajes: 5
Responder citando

Muchas gracias, explorer.

Ahora sí que funciona correctamente. Gracias por la ayuda y por las explicaciones.
Publicar nuevo tema   Responder al tema    Foros de discusión -> Básico Todas las horas son GMT - 6 Horas
Página 1 de 1



Powered by phpBB © 2001, 2005 phpBB Group