2009-02-14 13 views
10

Me gustaría saber qué patrón puedo usar en sed para hacer cambios en la primera línea de archivos de gran tamaño (~ 2 GB). La preferencia por sed es solo porque supongo que debe ser más rápido que un script de Python o Perl.¿Cómo puedo hacer cambios solo en la primera línea de un archivo?

Los archivos tienen la siguiente estructura:

field 1, field 2, ... field n 
data 

y, dada la probabilidad de tener espacios en el identificador para cada campo, tengo que sustituir cada espacio por un guión de esta manera:

**BEFORE** 
the first name,the second name,the first surname,a nickname, ... 
data 

**AFTER** 
the_first_name,the_second_name,the_first_surname,a_nickname, ... 
data 

Cualquier puntero al patrón correcto para usar u otra solución de guiones sería genial.

Respuesta

19

Para editar las primeras 10 líneas

sed -i -e '1,10s/ /_/g' 

En Perl, puede utilizar el operador flip-flop en el contexto escalar:

perl -i -pe 's/ /_/g if 1 .. 10' 
+0

Eso requiere una 'g' en el extremo para que reemplace todos los espacios en la línea, no solo el primero. –

+1

perl -i -pe 's// _/g si 1 .. 10' ??? Vaya, nunca escuché esta sintaxis en "si 1..10". A veces me molesta un poco con Perl. ¿Por qué todas estas excepciones? ¿Por qué no usar simplemente un simple if ($. <11)? – Frank

+0

@leon: wow, truco muy bueno !, gracias. –

5

Es poco probable que note una diferencia de velocidad entre Perl, Python, y sed. Su script pasará la mayor parte del tiempo esperando IO.

Si las líneas tienen la misma longitud, puede editar in situ, de lo contrario, tendrá que crear un nuevo archivo.

En Perl:

#!/usr/bin/env perl 
use strict; 

my $filename = shift; 
open my $in_fh, '<', $filename 
    or die "Cannot open $filename for reading: $!"; 
my $first_line = <$in_fh>; 

open my $out_fh, '>', "$filename.tmp" 
    or die "Cannot open $filename.tmp for writing: $!"; 

$first_line =~ s/some translation/goes here/; 

print {$out_fh} $first_line; 
print {$out_fh} $_ while <$in_fh>; # sysread/syswrite is probably better 

close $in_fh; 
close $out_fh; 

# overwrite original with modified copy 
rename "$filename.tmp", $filename 
    or warn "Failed to move $filename.tmp to $filename: $!"; 
+0

Hola, ¿puedes explicar por qué solo la primera línea se almacena en '$ first_line'? –

4

el cambio que usted menciona (en sustitución de cada espacio por un guión) no cambia la longitud de la línea, así que en teoría podría hacerse in-situ.

advertencia !: untestted!

head -n 1 yourfile | sed -e 's/ /_/g' > tmpfile 
dd conv=nocreat,notrunc if=tmpfile of=yourfile 

No estoy tan seguro de los parámetros conv=..., pero parece que debería hacer dd sobrescribir el inicio del archivo original con la línea transformada.

tenga en cuenta que si desea hacer cualquier otra transformación, que podría alterar la longitud de la línea, no lo haga, no haga esto. tendrías que hacer una copia completa. algo como esto:

head -n 1 yourfile | sed -e 's/ /_/g' > tmpfile 
tail -n + 2 | cat tmpfile - > transformedfile 
9

no lo hago piense que quiere usar cualquier solución que requiera que los datos se escriban en un nuevo archivo.

Si está bastante seguro de que todo lo que necesita es cambiar los espacios en caracteres de subrayado en la primera línea de los archivos de texto grandes, solo tiene que leer la primera línea, intercambiar los caracteres y volver a escribirlos en su lugar:

#!/usr/bin/env perl 
use strict; 

my $filename = shift; 
open (FH, "+< $filename") || die "can't open $filename: $!"; 
my $line = <FH>; 
$line =~ s/ /_/g; 
seek FH, 0, 0; # go back to the start of the file 
printf FH $line; 
close FH; 

para usarlo, sólo tiene que pasar la ruta completa del archivo para actualizar:

# fixheader "/path/to/myfile.txt" 
+0

Eso abre || el dado es incorrecto, evalúa a abrir FH, ("+ <$ filename" || morir "no se puede abrir $ filename: $!"); Ether use "or" o ponga paréntesis alrededor de los parámetros de abierto o ambos: abierto (FH, "+ <$ filename") o muere "no se puede abrir $ filename: $!"; – szabgab

+0

Es cierto, gracias por notar el error. –

+0

Esa iba a ser mi solución también. +1 – Axeman

-1

Esto podría ser una solución:

 

use Tie::File; 
tie my @array,"Tie::File","path_to_file"; 
$array[0] = "new text"; 
untie @array; 
 

Tie::File es uno de los módulos que uso más, y es muy fácil de usar. Cada elemento en la matriz es una línea en el archivo. Una de las desventajas, sin embargo, sería que esto carga todo el archivo en la memoria.

+0

en realidad no cargará el archivo si no es necesario, por lo que si solo cambia la primera línea y el número de caracteres no cambia, esto no tiene demasiados gastos generales. – szabgab

+0

Creo que es bastante raro tener el mismo número de caracteres después de una modificación de línea. – Geo

Cuestiones relacionadas