2010-12-06 18 views
96

Tengo un gran archivo A (que consta de correos electrónicos), una línea para cada correo. También tengo otro archivo B que contiene otro conjunto de correos.¿Cómo eliminar las líneas que aparecen en el archivo B de otro archivo A?

qué comando iba a utilizar para eliminar todas las direcciones que aparecen en el archivo B a partir del archivo A.

Por lo tanto, si el archivo A contenía:

A 
B 
C 

y archivo B contenía:

B  
D 
E 

luego presentar una debe quedar con:

A 
C 

Ahora sé que esta es una pregunta que podría haberse formulado con más frecuencia, pero solo encontré one command online que me dio un error con un delimitador incorrecto.

¡Cualquier ayuda sería muy apreciada! Alguien seguramente tendrá una idea inteligente, pero yo no soy el experto en proyectiles.

+0

posible duplicado de [Eliminar líneas de un archivo que están en otro archivo] (http://stackoverflow.com/questions/4780203/deleting-lines-from-one-file-which-are-in-another-file) – tripleee

+0

@tripleee Eso sí, el mío es un poco más viejo y el otro ha tenido votos para ser cerrado como una víctima de esto o ne – slhck

+1

La mayoría si las respuestas aquí son para archivos ordenados, y el más obvio falta, que por supuesto no es su culpa, pero que hace que el otro sea más útil en general. – tripleee

Respuesta

136
comm -23 file1 file2 

-23 suprime las líneas que están en ambos archivos, o sólo en el archivo 2. Los archivos tienen que ser ordenadas (que son en su ejemplo), pero si no, a través de la tubería sort primera ...

Véase el man page here

+3

'comm -23 archivo1 archivo2> archivo3' dará salida a contenidos en archivo1 no en archivo2, a archivo3. Y luego 'mv file3 file1' finalmente borraría los contenidos redundantes en file1. – 8090PZ

16

Otra forma de hacer lo mismo (también requiere de entrada ordenada):

join -v 1 fileA fileB 

En Bash, si no son pre-ordenados los archivos:

join -v 1 <(sort fileA) <(sort fileB) 
4

Usted puede hacer esto a menos que sus archivos se clasifican

diff file-a file-b --new-line-format="" --old-line-format="%L" --unchanged-line-format="" > file-a 

--new-line-format es para las líneas que se encuentran en el archivo B pero no en una --old-.. es para las líneas que se encuentran en un archivo, pero no en b --unchanged-.. es para líneas que están en ambos. %L lo hace para que la línea se imprima exactamente.

man diff 

para más detalles

+1

Usted dice que esto funcionará a menos que los archivos estén ordenados. ¿Qué problemas ocurren si están ordenados? ¿Qué pasa si están parcialmente ordenados? –

+1

Eso fue en respuesta a la solución anterior que sugería el uso del comando 'comm'. 'comm' requiere que los archivos sean ordenados, por lo tanto, si están ordenados, también puede usar esa solución. Puede usar esta solución independientemente de si el archivo está ordenado o no – aec

33

grep -Fvxf <lines-to-remove> <all-lines>

  • funciona en archivos sin ordenar
  • mantiene el orden
  • is POSIX

Ejemplo:

cat <<EOF > A 
b 
1 
a 
0 
01 
b 
1 
EOF 

cat <<EOF > B 
0 
1 
EOF 

grep -Fvxf B A 

Salida:

b 
a 
01 
b 

Explicación:

  • -F: utilizar las cadenas literales en lugar de la opción predeterminada BRE
  • -x: solamente partidos que responden a la totalidad línea
  • -v: imprimir no coincidentes
  • -f file: tomar los patrones del fichero dado

Este método es más lento en archivos preclasificados que otros métodos, ya que es más general. Si la velocidad importa, así, ver: Fast way of finding lines in one file that are not in another?

Ver también: https://unix.stackexchange.com/questions/28158/is-there-a-tool-to-get-the-lines-in-one-file-that-are-not-in-another

25

awk al rescate!

Esta solución no requiere entradas ordenadas. Primero debe proporcionar el archivo B.

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

vuelve

A 
C 

¿Cómo funciona?

NR==FNR{a[$0];next} modismo es para almacenar el primer archivo de una matriz asociativa como claves para una tarde "contiene" prueba.

NR==FNR está comprobando si estamos escaneando el primer archivo, donde el contador de línea global (NR) es igual al contador de línea de archivo actual (FNR).

a[$0] añade la línea actual a la matriz asociativa como la clave, tenga en cuenta que este se comporta como un conjunto, donde no habrá ningún valores duplicados (teclas)

!($0 in a) ahora estamos en el siguiente archivo (s), in es una prueba contiene, aquí está comprobando si la línea actual está en el conjunto que llenamos en el primer paso del primer archivo, ! niega la condición. Lo que falta aquí es la acción, que por defecto es {print} y generalmente no está escrita explícitamente.

Tenga en cuenta que esto ahora se puede utilizar para eliminar las palabras incluidas en la lista negra.

$ awk '...' badwords allwords > goodwords 

con un ligero cambio, puede limpiar varias listas y crear versiones limpias.

$ awk 'NR==FNR{a[$0];next} !($0 in a){print > FILENAME".clean"}' bad file1 file2 file3 ... 
+0

con las marcas completas en este. Para usar esto en la línea de comando en GnuWin32 en Windows, reemplace los nibbles simples con comillas dobles. funciona un placer muchas gracias. – twobob

+0

Esto funciona, pero ¿cómo podré redirigir la salida a fileA en forma de A (con una nueva línea) B –

+0

Supongo que te refieres a 'A \ nC', escribir primero en un archivo temporal y sobrescribir el archivo original' ...> tmp && mv tmp fileA' – karakfa

3

Este refinamiento de la respuesta de @ karakfa es notablemente más rápido en el caso de archivos muy grandes. Al igual que con esa respuesta, ninguno de los archivos debe ser ordenado, pero la velocidad está asegurada gracias a las matrices asociativas de awk. Solo el archivo de búsqueda se guarda en la memoria.

Esta formulación también permite la posibilidad de que solo se use un campo en particular ($ N) en el archivo de entrada en la comparación.

# Print lines in the input unless the value in column $N 
# appears in a lookup file, $LOOKUP; 
# if $N is 0, then the entire line is used for comparison. 

awk -v N=$N -v lookup="$LOOKUP" ' 
    BEGIN { while (getline < lookup) { dictionary[$0]=$0 } } 
    !($N in dictionary) {print}' 

(Otra ventaja de este enfoque es que es fácil de modificar el criterio de comparación, por ejemplo, para recortar iniciales y finales espacios en blanco.)

+0

Esto es más difícil de usar en un escenario de plataforma cruzada de caso-esquina que el otro revestimiento. Sin embargo, sombreros para el esfuerzo de rendimiento – twobob

0

Puede usar Python:

python -c ' 
lines_to_remove = set() 
with open("file B", "r") as f: 
    for line in f.readlines(): 
     lines_to_remove.add(line.strip()) 

with open("file A", "r") as f: 
    for line in [line.strip() for line in f.readlines()]: 
     if line not in lines_to_remove: 
      print(line) 
' 
Cuestiones relacionadas