dividir el archivo en dos partes, en un patrón

14

¿Cómo dividir un archivo grande en dos partes, en un patrón?

Dado un ejemplo file.txt:

ABC
EFG
XYZ
HIJ
KNL

Quiero dividir este archivo en XYZtal que file1contenga líneas hasta XYZy el resto de las líneas file2.

d.putto
fuente
¿Debería XYZincluirse la línea en la salida o no?
terdon
@terdon En mi caso, ninguna línea "XYZ" no debería formar parte del archivo2. Pero si tiene una manera de hacerlo, agregue para responder. Puede ser útil en algunos otros casos.
d.putto
Bastante justo, hecho.
terdon

Respuestas:

10

Con awkusted puede hacer:

awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile


Explicación: El primer awkargumento ( out=file1) define una variable con el nombre de archivo que se utilizará para la salida mientras largefilese procesa el argumento posterior ( ). El awkprograma imprimirá todas las líneas en el archivo especificado por la variable out( {print >out}). Si se encuentra el patrón XYZ, la variable de salida se redefinirá para que apunte al nuevo archivo ( {out="file2}") que se utilizará como destino para imprimir las líneas de datos posteriores.

Referencias

Janis
fuente
14

Este es un trabajo para csplit:

csplit -sf file -n 1 large_file /XYZ/

sería silently dividir el archivo, creando piezas con pre fIX filey numbered utilizando un solo dígito, por ejemplo, file0etc Tenga en cuenta que el uso de /regex/partiría hasta, pero sin incluir la línea que coincidencias regex. Para dividir e incluir la coincidencia de línea, regexagregue un +1desplazamiento:

csplit -sf file -n 1 large_file /XYZ/+1

Esto crea dos archivos file0y file1. Si realmente necesita que se nombren file1y file2siempre puede agregar un patrón vacío al csplitcomando y eliminar el primer archivo:

csplit -sf file -n 1 large_file // /XYZ/+1

crea file0, file1y file2aunque file0está vacío para que pueda quitar de forma segura:

rm -f file0
don_crissti
fuente
Esta, creo, es la respuesta más simple. Todo lo que tiene que hacer es enumerar algunos patrones y el archivo se dividirá por ellos en orden. ¡Brillante!
Henry Blyth el
6

Con un moderno, kshaquí hay una variante de shell (es decir, sin sed) de una de las sedrespuestas basadas arriba:

{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1


Y otra variante en kshsolo (es decir, también omitiendo cat):

{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1


(La kshsolución pura parece ser bastante eficaz; en un archivo de prueba de 2.4 GB necesitaba 19-21 segundos, en comparación con 39-47 segundos con el enfoque sed/ catbasado).

Janis
fuente
Es muy rapido. Pero no creo que lo necesite ready print, simplemente debe dejarlo ir a la salida por sí solo. El rendimiento mejora si construye el conjunto de herramientas AST por completo y kshcompila todos los componentes incorporados; es extraño para mí que sedno sea uno de ellos, en realidad. Pero con cosas como while <file dosupongo que no es necesario sedtanto ...
mikeserv
Sin embargo, tengo curiosidad: ¿cómo se awkdesempeñó en su punto de referencia? Y aunque estoy bastante seguro de kshque siempre ganará esta pelea, si está utilizando un GNU con el sedque no es muy justo sed: -unbuffered de GNU es un enfoque pobre para POSIXLY garantizar que el desplazamiento del descriptor se deja donde el programa se cerró - no debería ser necesario ralentizar el funcionamiento regular del programa - el almacenamiento en búfer está bien - todo lo que seddebe hacer es buscar el descriptor cuando haya terminado. Por alguna razón, GNU revierte esa mentalidad.
mikeserv
@mikeserv; La coincidencia del patrón de redirección se realiza hasta que se encuentra el patrón, y la línea con el patrón encontrado no se imprimirá si no se hace explícitamente como se muestra. (Al menos eso mostró mi prueba). Tenga en cuenta que no hay while; la impresión se realiza implícitamente como el efecto secundario definido del <##operador de redireccionamiento. Y solo se necesita imprimir la línea correspondiente. (De esa forma, la implementación de la función de shell es más flexible para admitir incl./excl.) Un whileciclo explícito que esperaría que fuera significativamente más lento (pero no lo he verificado).
Janis
1
@mikeserv; Ah bien. Por cierto, acabo de probar el en headlugar de la read; parece que sólo un poco más lento, pero más concisa de código: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
Janis
1
@mikeserv; Buen punto; no lo fue. Pero cuando activo el incorporado (solo hecho y comprobado los resultados) son los mismos números, curiosamente. (¿Tal vez alguna sobrecarga de llamada de función en comparación con la lectura?)
Janis
6
{ sed '/XYZ/q' >file1; cat >file2; } <infile

Con GNU seddeberías usar el -uinterruptor nbuffered. Sin sedembargo, la mayoría de los otros s deberían funcionar.

Para dejar a XYZ afuera ...

{ sed -n '/XYZ/q;p'; cat >file2; } <infile >file1
mikeserv
fuente
3

Prueba esto con GNU sed:

sed -n -e '1,/XYZ/w file1' -e '/XYZ/,${/XYZ/d;w file2' -e '}' large_file
Ciro
fuente
Más corto:sed -e '1,/XYZ/{w file1' -e 'd}' large_file > file2
don_crissti
1

Un truco fácil es imprimir ya sea en STDOUT o STDERR, dependiendo de si el patrón de destino ha coincidido. Luego puede usar los operadores de redirección del shell para redirigir la salida en consecuencia. Por ejemplo, en Perl, suponiendo que se llama al archivo de entrada fy los dos archivos de salida f1y f2:

  1. Descartando la línea que coincide con el patrón dividido:

    perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
  2. Incluyendo la línea coincidente:

    perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2

Alternativamente, imprima en diferentes identificadores de archivo:

  1. Descartando la línea que coincide con el patrón dividido:

    perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
    if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
  2. Incluyendo la línea coincidente:

    perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
              $a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
terdon
fuente