Estoy usando Solaris 10 y, por lo tanto, las opciones de grep que involucran -f no funcionan.
Tengo dos archivos separados por tuberías:
archivo1:
abc|123|BNY|apple|
cab|234|cyx|orange|
def|kumar|pki|bird|
archivo 2:
abc|123|
kumar|pki|
cab|234
Me gustaría comparar las dos primeras columnas del archivo 2 con el archivo 1 (buscar en todo el contenido del archivo 1 en las dos primeras columnas) si coinciden, imprima la línea coincidente del archivo 1. Luego busque la segunda línea del archivo 2 y así sucesivamente.
Salida esperada:
abc|123|BNY|apple|
cab|234|cyx|orange|
Los archivos que tengo son enormes, contienen unas 400.000 líneas, por lo que me gustaría que la ejecución fuera rápida.
Respuesta aceptada:
Esto es para lo que se diseñó awk:
$ awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0' file2 file1
abc|123|BNY|apple|
cab|234|cyx|orange|
Explicación
-F'|'
:establece el separador de campo en|
.NR==FNR
:NR es el número de línea de entrada actual y FNR el número de línea del archivo actual. Los dos serán iguales solo mientras se lee el primer archivo.-
c[$1$2]++; next
:si este es el primer archivo, guarde los primeros dos campos en elc
formación. Luego, salte a la siguiente línea para que esto solo se aplique en el primer archivo. -
c[$1$2]>0
:el bloque else solo se ejecutará si este es el segundo archivo, por lo que verificamos si los campos 1 y 2 de este archivo ya se han visto (c[$1$2]>0
) y si lo han sido, imprimimos la línea. Enawk
, la acción predeterminada es imprimir la línea, por lo que sic[$1$2]>0
es verdadero, la línea se imprimirá.
Alternativamente, ya que etiquetó con Perl:
perl -e 'open(A, "file2"); while(<A>){/.+?|[^|]+/ && $k{$&}++};
while(<>){/.+?|[^|]+/ && do{print if defined($k{$&})}}' file1
Explicación
La primera línea abrirá file2
, lee todo hasta el 2° |
(.+?|[^|]+
) y guárdelo (el $&
es el resultado del último operador de coincidencia) en el %k
hash.
La segunda línea procesa el archivo 1, usa la misma expresión regular para extraer las dos primeras columnas e imprime la línea si esas columnas están definidas en %k
hash.
Ambos enfoques anteriores deberán contener las 2 primeras columnas de file2 en la memoria. Eso no debería ser un problema si solo tiene unos cientos de miles de líneas, pero si lo es, podría hacer algo como
cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done
Pero eso será más lento.
Relacionado:¿Copiar todos los programas y archivos instalados en un disco duro (que tiene Windows 7 de 32 bits) y clonarlos/transferirlos a otra computadora que tenga Windows 7 de 64 bits?