2010-03-24 25 views
51

Tengo dos archivos A - nodes_to_delete y B - nodes_to_keep. Cada archivo tiene muchas líneas con identificadores numéricos.bash, Linux: establecer la diferencia entre dos archivos de texto

Quiero tener la lista de identificadores numéricos que están en nodes_to_delete pero NO en nodes_to_keep, p. alt text http://mathworld.wolfram.com/images/equations/SetDifference/Inline1.gif.

Hacerlo en una base de datos PostgreSQL es irrazonablemente lento. ¿Alguna forma ordenada de hacerlo en bash usando las herramientas CLI de Linux?

ACTUALIZACIÓN: Esto parece ser un trabajo Pythonic, pero los archivos son realmente, muy grandes. He resuelto algunos problemas similares usando uniq, sort y algunas técnicas de teoría de conjuntos. Esto fue aproximadamente dos o tres órdenes de magnitud más rápido que los equivalentes de la base de datos.

+0

tengo curiosidad en cuanto a qué respuestas vendrán. Bash es un poco más segphault, administrador de sistemas, creo. Si hubieras dicho "en python" o "en PHP" o lo que sea, tus posibilidades habrían sido mejores :) – extraneon

+0

Vi el título y estaba listo para criticar las inconsistencias de UI y los foros de ayuda de Holier-than-thou. Esto me decepcionó cuando leí la pregunta real. :( – aehiilrs

Respuesta

83

El comando comm hace eso.

+9

Y si los archivos aún no están ordenados, 'ordenar' primero. – extraneon

+2

+1 Enlightened, gran herramienta que me siento estúpido por no haberlo sabido. ¡Gracias! –

+5

@Adam Matan: mucha más información disponible en' ls/bin/usr/bin | xargs man' –

1

Tal vez necesites una mejor manera de hacerlo en postgres, puedo apostar que no encontrarás una forma más rápida de hacerlo con archivos planos. Debería poder realizar una unión interna simple y asumir que ambos id cols están indexados, lo que debería ser muy rápido.

+0

Usted es técnicamente correcto, y la' explicación 'respalda su reclamo, pero simplemente no funciona para mesas muy grandes (~ decenas de millones). –

+1

Sí, su memoria lo limitaría, a diferencia de algo así como un comunicador ordenado, pero creo que si tiene dos tablas con solo un campo de identificación int. Puede obtener 10s de millones sin problemas. –

+0

Eso es correcto en teoría, pero simplemente no funciona por alguna razón. –

26

Alguien me mostró cómo hacer exactamente esto hace un par de meses, y luego no pude encontrarlo por un tiempo ... y mientras miraba me tropecé con tu pregunta. Aquí está:

set_union() { 
    sort $1 $2 | uniq 
} 

set_difference() { 
    sort $1 $2 $2 | uniq -u 
} 

set_symmetric_difference() { 
    sort $1 $2 | uniq -u 
} 
+1

Creo que esto es mejor que la respuesta aceptada ... '' comm'' no está disponible en todos los entornos. – danwyand

+0

Y 'comm' tampoco funciona en' stdin' – wieczorek1990

+3

Es una diferencia simétrica, no una diferencia de conjunto normal. – Tgr

1

uso comm - comparará dos líneas archivos ordenados por línea de

La respuesta a la pregunta de OP utilizando este ejemplo de configuración aparece a continuación. Este comando devolverá líneas únicas para deleteNodes, no en keepNodes

comm -1 -3 <(sort keepNodes) <(sort deleteNodes) 

explicación: Mostrar líneas única para deleteNodes, ocultar otras líneas


ejemplo de configuración

Usaremos keepNodes y deleteNodes. Se usan como entrada sin clasificar.

$ cat > keepNodes <(echo bob; echo amber;) 
$ cat > deleteNodes <(echo bob; echo ann;) 

Por defecto sin argumentos, imprime comm 3 columnas

unique_to_FILE1 
    unique_to_FILE2 
     lines_appear_in_both 

Este es un ejemplo de barebones de comm sin argumentos. Tenga en cuenta las tres columnas.

$ comm <(sort keepNodes) <(sort deleteNodes) 
amber 
    ann 
     bob 

Supresión de salida de la columna

reprimir la columna 1, 2 o 3 con -N; tenga en cuenta que cuando una columna está oculta, el espacio en blanco se encoge.

$ comm -1 <(sort keepNodes) <(sort deleteNodes) 
ann 
    bob 
$ comm -2 <(sort keepNodes) <(sort deleteNodes) 
amber 
    bob 
$ comm -3 <(sort keepNodes) <(sort deleteNodes) 
amber 
    ann 
$ comm -1 -3 <(sort keepNodes) <(sort deleteNodes) 
ann 
$ comm -2 -3 <(sort keepNodes) <(sort deleteNodes) 
amber 
$ comm -1 -2 <(sort keepNodes) <(sort deleteNodes) 
bob 

Se fallará con gracia cuando se olvida de ordenar

comm: file 1 is not in sorted order

+0

+1 para ejemplos correctos que incluyen la respuesta a la pregunta específica de OP (líneas de salida en 'deleteNodes' que no están en' keepNodes'), pero sería mejor si se resaltara la solución correcta: 'comm -1 -3 <(ordenar keepNodes) <(sort deleteNodes) '. –

1

comm fue diseñado específicamente para este tipo de casos de uso, pero requiere de entrada ordenada.

awk es posiblemente una mejor herramienta para esto, ya que es bastante sencillo encontrar la diferencia establecida, no requiere sort, y ofrece flexibilidad adicional.

awk 'NR == FNR { a[$0]; next } !($0 in a)' nodes_to_keep nodes_to_delete 

Tal vez, por ejemplo, desea sólo para encontrar la diferencia en las líneas que representan los números no negativos:

awk -v r='^[0-9]+$' 'NR == FNR && $0 ~ r { 
    a[$0] 
    next 
} $0 ~ r && !($0 in a)' nodes_to_keep nodes_to_delete 
Cuestiones relacionadas