Como remover as linhas que aparecem no arquivo B de outro arquivo A?

Eu tenho um arquivo grande A (que consiste em e-mails), uma linha para cada e-mail. Eu também tenho outro arquivo B que contém outro conjunto de e-mails.

Qual comando eu usaria para remover todos os endereços que aparecem no arquivo B do arquivo A.

Então, se o arquivo A continha:

A B C 

e o arquivo B continha:

 BD E 

Então o arquivo A deve ficar com:

 A C 

Agora eu sei que esta é uma pergunta que poderia ter sido feita com mais frequência, mas eu só encontrei um comando online que me deu um erro com um delimitador ruim.

Qualquer ajuda seria muito apreciada! Alguém certamente vai aparecer com uma linha inteligente, mas eu não sou o especialista em shell.

 comm -23 file1 file2 

-23 suprime as linhas que estão em ambos os arquivos, ou apenas no arquivo 2. Os arquivos devem ser classificados (eles estão no seu exemplo), mas se não, encaminhá-los através de sort primeiro …

Veja a página man aqui

grep -Fvxf

  • funciona em arquivos não classificados
  • mantém o pedido
  • é POSIX

Exemplo:

 cat < A b 1 a 0 01 b 1 EOF cat < B 0 1 EOF grep -Fvxf BA 

Saída:

 b a 01 b 

Explicação:

  • -F : use strings literais em vez do padrão BRE
  • -x : considera apenas correspondências que correspondam à linha inteira
  • -v : imprimir correspondência não
  • -f file : pega padrões do arquivo dado

Esse método é mais lento em arquivos pré-classificados do que outros methods, já que é mais geral. Se a velocidade também importa, veja: Maneira rápida de encontrar linhas em um arquivo que não estão em outro?

Consulte também: https://unix.stackexchange.com/questions/28158/is-there-a-tool-to-get-the-lines-in-one-file-that-are-not-in-other

awk para o resgate!

Esta solução não requer inputs classificadas. Você precisa fornecer o arquivo B primeiro.

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

retorna

 A C 

Como funciona?

NR==FNR{a[$0];next} idioma é para armazenar o primeiro arquivo em uma matriz associativa como chaves para um teste posterior “contém”.

NR==FNR está verificando se estamos verificando o primeiro arquivo, onde o contador de linha global (NR) é igual ao atual contador de linha de arquivo (FNR).

a[$0] adiciona a linha atual à matriz associativa como chave, observe que ela se comporta como um conjunto, em que não haverá valores duplicados (chaves)

!($0 in a) estamos agora no (s) próximo (s) arquivo (s), in é um teste contém, aqui está verificando se a linha atual está no conjunto que preenchemos no primeiro passo do primeiro arquivo ! nega a condição. O que falta aqui é a ação, que por padrão é {print} e geralmente não é escrita explicitamente.

Observe que isso agora pode ser usado para remover palavras na lista negra.

 $ awk '...' badwords allwords > goodwords 

com uma pequena alteração, pode limpar várias listas e criar versões limpas.

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

Outra maneira de fazer a mesma coisa (também requer input classificada):

 join -v 1 fileA fileB 

No Bash, se os arquivos não forem pré-classificados:

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

Você pode fazer isso a menos que seus arquivos sejam classificados

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

--new-line-format é para linhas que estão no arquivo b, mas não em um --old-.. é para linhas que estão no arquivo a, mas não em b – --unchanged-.. é para linhas que estão em ambos . %L faz com que a linha seja impressa exatamente.

 man diff 

para mais detalhes

Esse refinamento da resposta legal do @karakfa pode ser notavelmente mais rápido para arquivos muito grandes. Como com essa resposta, nenhum arquivo precisa ser classificado, mas a velocidade é assegurada em virtude dos arrays associativos do awk. Apenas o arquivo de pesquisa é mantido na memory.

Essa formulação também permite a possibilidade de que apenas um campo específico ($ N) no arquivo de input seja usado na comparação.

 # 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}' 

(Outra vantagem dessa abordagem é que é fácil modificar o critério de comparação, por exemplo, para aparar espaços em branco à esquerda e à direita.)

Você pode usar o 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) ' 

Você pode usar – diff fileA fileB | grep "^>" | cut -c3- > fileA diff fileA fileB | grep "^>" | cut -c3- > fileA

Isso funcionará para arquivos que não são classificados também.