Compare dos archivos con la primera columna y elimine la fila duplicada del segundo archivo en el script de shell

9

Haré mi pregunta con un ejemplo. Tengo 2 archivos:

Archivo n. ° 1:

118D FC300_R5_TP  FX.B      32775       2112   6       2038   6       2112   0
118E FC300_R5_TP  FX.B      32775       2136   7       2065   6       2136   0
118F FC300_R5_TP  FX.B      32775       2124   6       2064   6       2124   0
1190 FC300_R5_TP  FX.B     819210     814632  99     814609  99     814632   0
1191 FC300_R5_TP  FX.B     819210     104100  13     103714  13     104100   0
1192 FC300_R5_TP  FX.B    1638420    1609476  98    1609402  98    1609476   0
1196 FC300_R5_TP  FX.B    1638420    1638432 100    1638379 100    1638432   0
119A FC300_R5_TP  FX.B    3276840    3271776 100    3271698 100    3271776   0
119E FC300_R5_TP  FX.B    3276840    3264120 100    3264034 100    3264120   0
11A2 FC300_R5_TP  FX.B    3276840    2328648  71    2328546  71    2328648   0
11A6 FC300_R5_TP  FX.B    3276840    2328444  71    2328355  71    2328444   0
11AA FC300_R5_TP  FX.B    3276840    2328528  71    2328403  71    2328528   0
11AE FC300_R5_TP  FX.B    3276840    2328648  71    2328468  71    2328648   0
11B2 FC300_R5_TP  FX.B    3276840    2130000  65    2129766  65    2130000   0
173A FC300_R5_TP  FX.B    6553680    6478572  99    6477747  99    6478572   0

Archivo # 2:

11AA FC300_R5_TP  FX.B    3276840    2328528  71    2328403  71    2328528   0
11AE FC300_R5_TP  FX.B    3276840    2328648  71    2328468  71    2328648   0
11B2 FC300_R5_TP  FX.B    3276840    2130000  65    2129766  65    2130000   0
173A FC300_R5_TP  FX.B    6553680    6478572  99    6477747  99    6478572   0
0BDB FC600_R5_TP  FX.B   33554640    6044364  18    6033105  18    6044364   0
0BDC FC600_R5_TP  FX.B   33554640    6613536  20    6481974  19    6613536   0
0BDD FC600_R5_TP  FX.B   33554640    4435848  13    4057170  12    4435848   0
0BDE FC600_R5_TP  FX.B   33554640    6620868  20    6249518  19    6620868   0

Salida deseada

Archivo # 3:

0BDB FC600_R5_TP  FX.B   33554640    6044364  18    6033105  18    6044364   0
0BDC FC600_R5_TP  FX.B   33554640    6613536  20    6481974  19    6613536   0
0BDD FC600_R5_TP  FX.B   33554640    4435848  13    4057170  12    4435848   0
0BDE FC600_R5_TP  FX.B   33554640    6620868  20    6249518  19    6620868   0

Me gustaría comparar el archivo 1 y el archivo 2 usando sus primeras columnas y eliminar toda la línea o fila del archivo 2 donde coinciden en el archivo 1. También me gustaría guardar los resultados en un tercer archivo, archivo # 3.

Rahul Rana
fuente

Respuestas:

10

Puedes usar awkpara esto:

awk 'FNR==NR{a[$1];next};!($1 in a)' file1 file2 > file3

Explicación:

  • FNR == NR: Esta prueba es verdadera cuando el número de registros es igual al número de registros en el archivo. Esto solo es cierto para el primer archivo, ya que el segundo archivo NRserá igual al número de líneas del archivo1 + FNR.

  • a[$1]: Cree un índice de elemento de matriz del primer campo del archivo1.

  • next: salta al siguiente registro para que no se realice más procesamiento en el archivo1.

  • !($1 in a): Vea si el primer campo ($ 1) está presente en la matriz, es decir, en el archivo1, e imprima la línea completa (al archivo3).

Basado en uno de los ejemplos de la wiki #awk .

jasonwryan
fuente
¡¡¡Respuesta perfecta!!!
mtk
8
export LC_ALL=C
comm -13 <(sort f1) <(sort  f2)

Informaría las líneas que solo están en f2.

export LC_ALL=C
join -v2 <(sort f1) <(sort f2)

Informaría las líneas de f2cuyo primer campo no se encuentra como el primer campo en cualquier línea de f1.

(necesita un shell con soporte para la sustitución de procesos como ksh93, zsho bash).

Stéphane Chazelas
fuente
2

Solo por diversión, aquí hay una solución en Perl:

#!/usr/bin/perl

# create names lookup table from first file
my %names;
while (<>) {
    (my $col1)= split / /, $_;
    $names{$col1} = 1;
    last if eof;
}

# scan second file
while (<>) {
    print if /^(\S+).*/ && not $names{$1};
}

Ejemplo

$ ./showdiffs.pl file1  file2
0BDB FC600_R5_TP  FX.B   33554640    6044364  18    6033105  18    6044364   0
0BDC FC600_R5_TP  FX.B   33554640    6613536  20    6481974  19    6613536   0
0BDD FC600_R5_TP  FX.B   33554640    4435848  13    4057170  12    4435848   0
0BDE FC600_R5_TP  FX.B   33554640    6620868  20    6249518  19    6620868   0

Detalles

La solución Perl anterior se compone de 2 bucles. El primer bucle lee todas las líneas desde file1y crea un hash, %namesdonde se agrega cada columna que identificamos.

$names{11AA} = 1;

El segundo whileciclo luego se ejecuta sobre el segundo archivo, file2y la columna 1 de cada línea se identifica usando la expresión regular:

^(\S+).*

Lo anterior dice desde el principio de la línea, hacer coincidir todo lo que no sea un espacio y guardarlo en la variable temporal $1. Se guarda envolviendo los parens a su alrededor. El .*dice que coincida con todo lo demás en la línea.

El siguiente bit de esas líneas dice que busque el bit de columna 1 que acabamos de guardar $1en el %nameshash:

$names{$1}

Si está presente allí, entonces no queremos imprimirlo. Si no está allí, imprímalo.

slm
fuente
2

Método 1 # Bash

#!/usr/bin/env bash
file1=$1
file2=$2

[[ $# -ne 2 ]]  && { echo -e "\n\tUsage: \t$0 file1 file2\n"; exit 1; }

while read line
do

        if ! grep -q "${line%% .*}" $file1; then
                echo "${line}"
        fi

done < $file2

Método 2 # Solo Grep

grep -v "$(< file1)" file2

grep funciona, pero no es garantía

Rahul Patil
fuente
1

Vamos a obtenerlo como

Archivo n. ° 1: archivo1.txt

Archivo n. ° 2: file2.txt

Luego corre siguiendo en la terminal

fgrep -vf test1.txt test2.txt > output.txt

output.txt contendrá los resultados deseados.

Explicación:

fgrep : print lines matching a pattern (from manual page)
-v  : get only non-matching rows
-f : obtain PATTERN from FILE (from manual page)
inckka
fuente
Esto solo funciona si las líneas completas son idénticas, pero el interrogador solicitó explícitamente una comparación solo en la primera columna.
Adaephon