Modificar archivos de texto [batch] [Perl]

Iniciado por bjeli1980, 11 Mayo 2010, 14:34 PM

0 Miembros y 2 Visitantes están viendo este tema.

bjeli1980

Hola,

Me ha surgido un problema que tengo que solucionar y no he dudado en recurrir a este foro, ya que otras veces me habeis echado una mano.

Tengo 16000 archivos de texto en una carpeta en los que les tengo que modificar un solo dato en cada archivo. Los archivos son de esta forma:

LST;1;
costados\koa9\koa9gx70;ko-//-a9-gx7-0 ; ;1;1;0;2278.;325.;18.;0;0;0;0;0;0;0;0;;;;
costados\koa9\koa9gx70;ko-//-a9-gx7-0 ;M;1;1;0;2278.;325.;18.;0;0;0;0;0;0;0;0;;;;
wa;baldas fondo 2, 3, 5 y 6;;0;0;1;1162.;280.;18.;0;0;;;;;;;;;;
plb;puerta común batiente ;;0;0;1;2275.;297.;20.;0;1;2;192;0;0;0;0;0;0;;

En aquellas líneas que aparece la palabra "puerta" o "frente" necesito restarle 1 al valor que está entre el sexto y septimo punto y coma, en este caso 2275.

Por lo que esa línea quedaría así y el resto del archivo estaría igual:
plb;puerta común batiente ;;0;0;1;2274.;297.;20.;0;1;2;192;0;0;0;0;0;0;;

Gracias antes de nada.

biribau

Es fácil si tienes perl
Código (perl) [Seleccionar]

my ($a, $_, $num, $b) = $s =~ m/(([^;]*;){6})([0-9]*)(.*)$/;
$num--;

cgvwzq

#2
Ya lo he modificado y he añadido el bucle que recorre el directorio:

Código (dos) [Seleccionar]
@echo off
setlocal enabledelayedexpansion

:main
set /p "dir=Directorio: "
for /f %%x in ('dir /b %dir%') do (call :fichero !dir! %%x)
goto:eof

:fichero
set "file=%1\%2"
for /f "tokens=*" %%_ in (%file%) do (
   ((echo %%_ | find "puerta" > nul) || (echo %%_ | find "frente" > nul)) && (call :linea "%%_") || (echo %%_ >> tmp.txt)
)
copy tmp.txt !file! > nul
echo  !file! hecho
del tmp.txt
goto:eof

:linea
for /f "delims=; tokens=1-6*" %%a in (%1) do (
   set "num=%%f" & set "num=!num:~0,-1!"
   set /a num-=1
   echo %%a;%%b;%%c;%%d;%%e;!num!.;%%g; >> tmp.txt
)
goto:eof


Al final he copiado el "num=!num:~0,-1!" de Leo, en lugar de "num=!num:.=!". Aunque funciona igual...
Some stuff:

  • www.a] parsed as www.a]
  • Bypass elhacker's img filter with ALT attribute!
  • ¿Para cuándo SQLi I y II? WZ



leogtz

Algunas preguntas.

Cada archivo solo contiene esto:
LST;1;
costados\koa9\koa9gx70;ko-//-a9-gx7-0 ; ;1;1;0;2278.;325.;18.;0;0;0;0;0;0;0;0;;;;
costados\koa9\koa9gx70;ko-//-a9-gx7-0 ;M;1;1;0;2278.;325.;18.;0;0;0;0;0;0;0;0;;;;
wa;baldas fondo 2, 3, 5 y 6;;0;0;1;1162.;280.;18.;0;0;;;;;;;;;;
plb;puerta com·n batiente ;;0;0;1;2275.;297.;20.;0;1;2;192;0;0;0;0;0;0;;


Es decir, ¿cada archivo solo contiene 1 vez la línea con "puerta"?

¿Podemos usar aplicaciones externas? (sed para Windows)
Código (perl) [Seleccionar]

(( 1 / 0 )) &> /dev/null || {
echo -e "stderrrrrrrrrrrrrrrrrrr";
}

http://leonardogtzr.wordpress.com/
leogutierrezramirez@gmail.com

leogtz

#4
He hecho esto:

Supongamos que nuestro archivo se llama "file.txt", y tiene un contenido igual al que pusiste:
Código (bash) [Seleccionar]

LST;1;
costados\koa9\koa9gx70;ko-//-a9-gx7-0 ; ;1;1;0;2278.;325.;18.;0;0;0;0;0;0;0;0;;;;
costados\koa9\koa9gx70;ko-//-a9-gx7-0 ;M;1;1;0;2278.;325.;18.;0;0;0;0;0;0;0;0;;;;
wa;baldas fondo 2, 3, 5 y 6;;0;0;1;1162.;280.;18.;0;0;;;;;;;;;;
plb;puerta com·n batiente ;;0;0;1;2275.;297.;20.;0;1;2;192;0;0;0;0;0;0;;


La salida que da mi script es:

LST;1;
costados\koa9\koa9gx70;ko-//-a9-gx7-0 ; ;1;1;0;2278.;325.;18.;0;0;0;0;0;0;0;0;;;;
costados\koa9\koa9gx70;ko-//-a9-gx7-0 ;M;1;1;0;2278.;325.;18.;0;0;0;0;0;0;0;0;;;;
wa;baldas fondo 2, 3, 5 y 6;;0;0;1;1162.;280.;18.;0;0;;;;;;;;;;
plb;puerta com·n batiente ;;0;0;1;2274.;297.;20.;0;1;2;192;0;0;0;0;0;0;;


El script es este:
Código (dos) [Seleccionar]

@echo off
setlocal enabledelayedexpansion
rem Saber en que linea vamos a cambiar:
for /f "tokens=1 delims=:" %%# in ('type "file.txt" ^| findstr /i /n "puerta"') do (
set "n_line=%%#"
)
rem La linea
for /f "tokens=*" %%_ in ('type "file.txt" ^| findstr /i "puerta"') do (
set "linea=%%_"
)
set /a "count=0"
for /f "tokens=*" %%_ in (file.txt) do (
set /a count+=1
rem Mostrar normalmente:
if !count! lss !n_line! (
echo %%_
)
rem Sino, procesar:
if !count! equ !n_line! (
for /f "delims=; tokens=1-6*" %%a in ('echo !linea!') do (
set "numero=%%f"
set "numero=!numero:~0, -1!"
set /a numero-=1
echo %%a;%%b;;%%c;%%d;%%e;!numero!.;%%g
)
)
)


Obviamente tienes que volcar la salida a un archivo de texto nuevo, luego si quieres renombras.

Edit, esto también funciona:

Código (bash) [Seleccionar]
@echo off
setlocal enabledelayedexpansion
for /f "delims=: tokens=1-2*" %%a in ('type "file.txt" ^| findstr /i /n "puerta"') do (
set "n_line=%%a"
)
for /f "tokens=*" %%_ in ('type "file.txt" ^| findstr /i "puerta"') do (
set "linea=%%_"
)
for /f "delims=; tokens=1-6*" %%a in ('echo !linea!') do (
set "numero=%%f"
set "numero=!numero:~0, -1!"
set /a numero-=1
call:edit %%a "%%b" %%c %%d %%e !numero! "%%g"
goto:eof
)
:edit
::
(
echo %n_line%
echo %1;%~2;;%3;%4;%5;%6.;%~7 %~8
echo w
echo e
) | edlin /b file.txt > nul 2>&1
goto:eof



Lo edita en el mismo lugar.
Código (perl) [Seleccionar]

(( 1 / 0 )) &> /dev/null || {
echo -e "stderrrrrrrrrrrrrrrrrrr";
}

http://leonardogtzr.wordpress.com/
leogutierrezramirez@gmail.com

bjeli1980

#5
Gracias a todos por responder.

- biribau, había pensado en hacerlo en perl ya que en batch me resultaba más dificil, tengo q probar la solución q me das, pero primero tendría que buscar primero si la línea contiene la palabra "puerta" o "frente"

- Leo, si que podemos utilizar aplicaciones externas. La palabra puerta o frente podría aparecer en más de una línea, por lo que habría que cambiarlo en varias líneas.

Voy a probar lo que me habeis puesto.

;) ;) ;) ;)

Ya lo he probado pero no me funciona para un fichero concreto. Me falla dentro de este for: for /f "tokens=*" %%_ in (file.txt) do (

Leo tengo que cambiarlo en 16000 ficheros, por lo que tengo q hacerlo de manera que no tenga que editarlos. Puede que la palabra puerta o frente no esté en ninguna línea del fichero, también puede ser que esté en diferentes lineas.


bjeli1980

#6
Al final lo he hecho en Perl, os pongo el código:

Muchas gracias a todos:

Código (perl) [Seleccionar]

opendir(DIRHANDLE,".")||die "ERROR: no se puede leer directorio actual\n";
foreach (readdir(DIRHANDLE)){
next if ($_ eq "." || $_ eq "..");  
next if (-d "$_" && ! -l "$_");  
print $_ . "\n";
open (ENTRADA,"<$_") || die "ERROR: No puedo abrir el fichero $_\n";
#Los copio dentro la carpeta
open (SALIDA,">modificados/$_") || die "ERROR: No puedo abrir el fichero $d/$_\n";


while ($s=<ENTRADA>)
 {
my ($a, $_, $num, $b) = $s =~ m/(([^;]*;){6})([0-9]*)(.*)$/;

if (($s =~ /puerta/ ) || ($s =~ /frente/ ))
{
print "Encontrado";
$num--;
$s = $a . $_ . $num . $b;
}
print SALIDA $s;
 }
 close (ENTRADA);
 close (SALIDA);
}
closedir DIRHANDLE;

biribau

Cita de: bjeli1980 en 12 Mayo 2010, 12:29 PM
      $s = $a . $_ . $num . $b;
El $_ lo había puesto ahí precisamente para que se notase que había que descartarlo, seguro que te funciona?
Porque hay unos paréntesis de una subexpresión que no interesa, pero hay que ponerlos por asociatividad  :-\

leogtz

@bjeli1980

Como no habías especificado, lo hice para un solo archivo en concreto.
Hacerlo con 1600 archivos sería un engorro, además de que lentísimo.
Te felicito por la decisión de hacerlo en Perl.

Saludos.
Código (perl) [Seleccionar]

(( 1 / 0 )) &> /dev/null || {
echo -e "stderrrrrrrrrrrrrrrrrrr";
}

http://leonardogtzr.wordpress.com/
leogutierrezramirez@gmail.com

bjeli1980

Tienes razón, no está bien, voy a echarle un vistazo.