Verifique si todas las líneas de archivo aparecen en un archivo diferente


14

Obtuve dos archivos: archivo1 con aproximadamente 10 000 líneas y archivo2 con unos cientos de líneas. Quiero verificar si todas las líneas del archivo2 ocurren en el archivo1. Es decir: ∀ línea ℓ ∈ archivo2: ℓ ∈ archivo1

Si alguien no sabe lo que significan estos símbolos o qué significa "verificar si todas las líneas del archivo2 aparecen en el archivo1" significa: Varias líneas equivalentes en cualquiera de los archivos no influyen en si la verificación devuelve que los archivos cumplen con el requisito o no.

¿Cómo hago esto?


2
¿Pueden esos archivos tener líneas duplicadas? Si file2contiene 2 líneas A, ¿necesita file1contener al menos 2 líneas A?
Stéphane Chazelas

2
@ StéphaneChazelas Todas las líneas (en ambos archivos) tienen la garantía de ser únicas.
UTF-8

1
@ UTF-8 Ese sería un detalle importante para editar en su pregunta.
David Z

2
@DavidZ Ya no, ya que las respuestas existentes no se basan en esa garantía. Entonces, al editar la pregunta ahora, reduciría el alcance aparente de las respuestas.
UTF-8

@ UTF-8 Supongo que sí, aunque la pregunta es un poco ambigua sin ella, por ejemplo, si una línea dada aparece 5 veces en el archivo2, ¿esa línea también tiene que aparecer 5 veces en el archivo1 (en lugar de solo una vez)? Si tuviera ese requisito, no parece que ninguna de las respuestas existentes funcionaría, por lo que sugeriría al menos editar algo que deje en claro que eso no es lo que quiere decir.
David Z

Respuestas:


18
comm -13 <(sort -u file_1) <(sort -u file_2)

Este comando generará líneas exclusivas de file_2. Entonces, si la salida está vacía, entonces todas las file_2líneas están contenidas en file_1.

Del hombre de comunicación:

   With  no  options,  produce  three-column  output.  Column one contains
   lines unique to FILE1, column two contains lines unique to  FILE2,  and
   column three contains lines common to both files.

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)

@don_crissti Verdadero. Solucionado: la -uopción agregada al sortcomando. Ahora, solo quedan líneas únicas en ambos archivos ordenados.
MiniMax

Increíblemente simple solución! ¿Es aplicable esta sintaxis a cualquier programa que espere archivos? Siempre pensé que el <canalizado en stdin. ¿El término del soporte cambia esto?
UTF-8

2
@ UTF-8 Se llama sustitución de proceso . Puedes leer aquí al respecto. Y sí, se comporta como un archivo temporal, por lo que puede usarse en lugar de archivos reales en cualquier programa, que espera archivos.
MiniMax

Si esto es algo que haces con frecuencia, es posible que desees almacenarlo file_1en forma clasificada. Ahorra tiempo y mecanografía.
Stig Hemmer

77
@minimax Buen comentario, excepto el "any". La sustitución de procesos, aunque maravillosa, no se puede usar en todos los casos, porque los "archivos" resultantes son secuencias y no archivos reales. Esto significa que no son "buscables" como lo es un archivo normal, y solo se pueden usar cuando el programa lee el archivo normalmente desde el principio, y no cuando el programa usa alguna funcionalidad de solo archivo, como buscar un punto específico o rebobinado para comenzar de nuevo desde el principio. Afortunadamente, la mayoría de los programas simplemente leen () sus archivos, por lo que la sustitución de procesos funciona con la mayoría de los programas, pero no con "ninguno".
Ley29

7
[ $(grep -cxFf file2 <(sort -u file1)) = $(sort -u file2 | wc -l) ] && 
  echo all there || 
  echo some missing

Si el número de coincidencias del archivo2 en (las líneas únicas de) archivo1 es el mismo que el número de líneas únicas en el archivo2, entonces todos están allí; de lo contrario, no lo son.


5

Usar GNU awkdonde admite length(array)funciones específicas (y alguna otra awkimplementación que pueda admitir) y no es necesario si se ordenan los archivos.

gawk 'FNR==NR{seen[$0];next} ($0 in seen){delete seen[$0]};
    END{print (!length(seen))?"Matched":"Not Matched"}' file2 file1

Esto es leer file2 en una matriz llamada seencon la clave como la línea completa de file2 .

Luego lea el archivo1 y para cada línea si coincide con las líneas en la matriz que se ve, elimine esa clave.

Al final, si la matriz estaba vacía, significa que todas las líneas del archivo 2 existen en el archivo 1 y se imprimirán Matched, de lo contrario se mostrarán Not Matched.


Por la compatibilidad en todas las awkimplementaciones.

awk 'FNR==NR{seen[$0];next} ($0 in seen){delete seen[$0]};
    END{for(x in seen);print (!x)?"Matched":"Not Matched"}' file2 file1

Para ignorar líneas vacías / o líneas con espacios en blanco solo si está en el archivo 2 , necesitará agregar NFa la condición NR==FNR && NF {...para omitir su lectura en la matriz.


length(array)es solo gawk AFAIK; definitivamente no es POSIX.
dave_thompson_085

@ dave_thompson_085 Correcto, he actualizado mi respuesta. gracias
αғsнιη

3

Utilizando commpuede encontrar líneas que son comunes en ambos archivos.

comm -12 file1 file2

Echa un vistazo man commpara más detalles


Correcto, está devolviendo líneas comunes en ambos archivos, pero esto no proporciona una respuesta a la Q del OP, donde si tuviera una línea en el archivo2 que no sale en el archivo1, entonces todas las líneas del archivo2 no existen en el archivo1.
αғsнιη

1
Los archivos deben estar ordenados. From man " comm- compara dos archivos ordenados línea por línea".
MiniMax

@MiniMax tiene razón. Esto no funciona La otra respuesta que utiliza commcontiene una solución que obviamente no es incorrecta. Cuando ejecuto su comando, recibo advertencias de que los archivos no están ordenados y muchas líneas que definitivamente están en ambos archivos.
UTF-8

3
diff -q <(sort -u file2) <(grep -Fxf file2 file1 | sort -u)

producirá ninguna salida si file1contiene todas las líneas en file2y salir con el estado 0, de lo contrario será algo así como imprimir

Files /proc/self/fd/11 and /proc/self/fd/12 differ

y salir con estado 1


2

Use un programa de Python:

#!/usr/bin/env python3
import sys

def open_arg(path):
    return sys.stdin if path == '-' else open(path)

def strip_linebreak(s):
    return s[:-1] if s.endswith('\n') else s

with open_arg(sys.argv[1]) as pattern_file:
    patterns = set(map(strip_linebreak, pattern_file))

with open_arg(sys.argv[2]) as dataset_file:
    for l in map(strip_linebreak, dataset_file):
        patterns.remove(l)
        if not patterns:
            break

sys.exit(int(bool(patterns)))

Uso:

python3 contains-all.py file2 file1

El estado de salida del programa indica si todos los patrones del archivo 2 coincidieron:

  • 0 (éxito) significa que todos los patrones coincidieron.
  • 1 (falla) significa que algunos patrones no coincidieron.

Para consultar el estado de salida en una cáscara (script) que puede utilizar la $?variable especial u otras expresiones que evalúan el estado de salida de comandos, por ejemplo, operadores de cortocircuito &&y ||y expresiones condicionales como ifo while. Ejemplo:

if python3 compare-all.py file2 file1 && some-other --condition; then
    # do stuff
fi

1

combinefrom moreutils le mostrará todas las líneas file2que no están en file1:

combine file2 not file1

Luego puede contar el número de líneas canalizándolas wc -l, como:

if [ $(combine file2 not file1 | wc -l) != 0 ]; then
  echo "lines missing"
else
  echo "You're fine"
fi
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.