¿Cómo eliminar las líneas que aparecen en el archivo B de otro archivo A?

Resuelto slhck asked hace 13 años • 12 respuestas

Tengo un archivo A grande (que consta de correos electrónicos), una línea para cada correo. También tengo otro archivo B que contiene otro conjunto de correos.

¿Qué comando usaría para eliminar todas las direcciones que aparecen en el archivo B del archivo A?

Entonces, si el archivo A contenía:

A
B
C

y el archivo B contenía:

B    
D
E

Entonces el archivo A debería quedar con:

A
C

Ahora sé que esta es una pregunta que se podría haber hecho con más frecuencia, pero solo encontré un comando en línea que me dio un error con un delimitador incorrecto.

¡Cualquier ayuda sería muy apreciada! Seguramente a alguien se le ocurrirá una frase ingeniosa, pero no soy un experto en shell.

slhck avatar Dec 06 '10 19:12 slhck
Aceptado

Si los archivos están ordenados (están en su ejemplo):

comm -23 file1 file2

-23suprime las líneas que están en ambos archivos, o solo en el archivo 2. Si los archivos no están ordenados, canalícelos sortprimero...

Vea la página de manual aquí

The Archetypal Paul avatar Dec 06 '2010 12:12 The Archetypal Paul

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

  • funciona en archivos no ordenados ( a diferencia decomm )
  • mantiene el orden
  • es POSIX

Ejemplo:

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

cat <<EOF > B
0
1
EOF

grep -Fvxf B A

Producción:

b
a
01
b

Explicación:

  • -F: utilice cadenas literales en lugar del BRE predeterminado
  • -x: solo considera coincidencias que coincidan con toda la línea
  • -v: imprimir no coincidente
  • -f file: toma patrones del archivo dado

Este método es más lento en archivos preordenados que otros métodos, ya que es más general. Si la velocidad también importa, consulte: ¿ Forma rápida de encontrar líneas en un archivo que no están en otro?

Aquí hay una automatización de bash rápida para operación en línea:

remove-lines() (
  remove_lines="$1"
  all_lines="$2"
  tmp_file="$(mktemp)"
  grep -Fvxf "$remove_lines" "$all_lines" > "$tmp_file"
  mv "$tmp_file" "$all_lines"
)

GitHub ascendente .

uso:

remove-lines lines-to-remove remove-from-this-file

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

¡Awk al rescate!

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

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

devoluciones

A
C

¿Como funciona?

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

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

a[$0]agrega la línea actual a la matriz asociativa como clave, tenga en cuenta que esto se comporta como un conjunto, donde no habrá valores duplicados (claves)

!($0 in a)Ahora estamos en los siguientes archivos, ines una prueba de contenido, aquí se verifica si la línea actual está en el conjunto que completamos en el primer paso desde el primer archivo, !niega la condición. Lo que falta aquí es la acción, que de forma predeterminada {print}y generalmente no está escrita explícitamente.

Tenga en cuenta que esto ahora se puede utilizar para eliminar palabras de 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 ...
karakfa avatar Sep 23 '2015 19:09 karakfa