¿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.
¿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.
Respuestas:
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
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
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).
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 ...
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.
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).
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.
Prueba esto con GNU sed:
sed -n -e '1,/XYZ/w file1' -e '/XYZ/,${/XYZ/d;w file2' -e '}' large_file
sed -e '1,/XYZ/{w file1' -e 'd}' large_file > file2
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:
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>f2Incluyendo la línea coincidente:
perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2Alternativamente, imprima en diferentes identificadores de archivo:
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 "$_";' fIncluyendo la línea coincidente:
perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
$a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
XYZincluirse la línea en la salida o no?