linux perl bash scripting command-line

linux - extrayendo valores únicos entre 2 conjuntos/archivos



perl bash (7)

Trabajando en Linux / shell env, ¿cómo puedo lograr lo siguiente:

el archivo de texto 1 contiene:

1 2 3 4 5

el archivo de texto 2 contiene:

6 7 1 2 3 4

Necesito extraer las entradas en el archivo 2 que no están en el archivo 1. Así que ''6'' y ''7'' en este ejemplo.

¿Cómo hago esto desde la línea de comandos?

¡muchas gracias!


Aquí hay otra solución awk

$ awk ''FNR==NR{a[$0]++;next}(!($0 in a))'' file1 file2 6 7


Me preguntaba cuál de las siguientes soluciones era la "más rápida" para archivos "más grandes":

awk ''FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}'' file1 file2 # awk1 by SiegeX awk ''FNR==NR{a[$0]++;next}!($0 in a)'' file1 file2 # awk2 by ghostdog74 comm -13 <(sort file1) <(sort file2) join -v 2 <(sort file1) <(sort file2) grep -v -F -x -f file1 file2

Resultados de mis puntos de referencia en breve:

  • No use grep -Fxf , es mucho más lento (2-4 veces en mis pruebas).
  • comm es ligeramente más rápido que join .
  • Si file1 y file2 ya están ordenados, comm y join son mucho más rápidos que awk1 + awk2. (Por supuesto, no asumen archivos ordenados.)
  • awk1 + awk2, supuestamente, usa más RAM y menos CPU. Los tiempos de ejecución reales son más bajos para la comm probablemente debido al hecho de que utiliza más subprocesos. Los tiempos de CPU son más bajos para awk1 + awk2.

En aras de la brevedad omito todos los detalles. Sin embargo, asumo que cualquier persona interesada puede contactarme o simplemente repetir las pruebas. A grandes rasgos, la configuración era

# Debian Squeeze, Bash 4.1.5, LC_ALL=C, slow 4 core CPU $ wc file1 file2 321599 321599 8098710 file1 321603 321603 8098794 file2

Resultados típicos de las carreras más rápidas

awk2: real 0m1.145s user 0m1.088s sys 0m0.056s user+sys 1.144 awk1: real 0m1.369s user 0m1.324s sys 0m0.044s user+sys 1.368 comm: real 0m0.980s user 0m1.608s sys 0m0.184s user+sys 1.792 join: real 0m1.080s user 0m1.756s sys 0m0.140s user+sys 1.896 grep: real 0m4.005s user 0m3.844s sys 0m0.160s user+sys 4.004

Por cierto, para los awkies: Parece que a[$0]=1 es más rápido que a[$0]++ , y (!($0 in a)) es más rápido que (!a[$0]) . Por lo tanto, para una solución awk sugiero:

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


Qué tal si:

diff file_1 file_2 | grep ''^>'' | cut -c 3-

Esto imprimiría las entradas en el archivo_2 que no están en el archivo_1. Para el resultado opuesto, solo hay que reemplazar ''>'' con ''<''. ''cut'' elimina los dos primeros caracteres agregados por ''diff'', que no forman parte del contenido original.

Los archivos ni siquiera necesitan ser ordenados.


Si realmente está configurado para hacer esto desde la línea de comandos, este sitio (búsqueda de "no se encontraron duplicados") tiene un ejemplo awk que busca duplicados. Puede ser un buen punto de partida para mirar eso.

Sin embargo, te animo a que uses Perl o Python para esto. Básicamente, el flujo del programa sería:

findUniqueValues(file1, file2){ contents1 = array of values from file1 contents2 = array of values from file2 foreach(value2 in contents2){ found=false foreach(value1 in contents1){ if (value2 == value1) found=true } if(!found) print value2 } }

Esta no es la forma más elegante de hacerlo, ya que tiene una complejidad de tiempo O (n ^ 2), pero hará el trabajo.


Usando algunas utilidades menos conocidas:

sort file1 > file1.sorted sort file2 > file2.sorted comm -1 -3 file1.sorted file2.sorted

Esto generará duplicados, por lo tanto, si hay 1 3 en el file1 , pero 2 en el file2 , esto generará 1 3 . Si esto no es lo que quiere, canalice la salida de la sort través de uniq antes de escribirlo en un archivo:

sort file1 | uniq > file1.sorted sort file2 | uniq > file2.sorted comm -1 -3 file1.sorted file2.sorted

Hay muchas utilidades en el paquete GNU coreutils que permiten todo tipo de manipulaciones de texto.


con grep:

grep -F -x -v -f file_1 file_2


$ awk ''FNR==NR {a[$0]++; next} !a[$0]'' file1 file2 6 7

Explicación de cómo funciona el código:

  • Si estamos trabajando en file1, rastree cada línea de texto que vemos.
  • Si estamos trabajando en file2 y no hemos visto el texto de la línea, imprímalo.

Explicación de los detalles:

  • FNR es el número de registro del archivo actual
  • NR es el número de registro global actual de todos los archivos de entrada
  • FNR==NR es verdadero solo cuando estamos leyendo el archivo 1
  • $0 es la línea actual de texto
  • a[$0] es un hash con la clave establecida en la línea de texto actual
  • a[$0]++ pistas que hemos visto la línea actual de texto
  • !a[$0] es verdadero solo cuando no hemos visto la línea de texto
  • Imprima la línea de texto si el patrón anterior devuelve verdadero, este es el comportamiento awk predeterminado cuando no se realiza ninguna acción explícita