Existe alguma ferramenta que possa obter linhas que o arquivo A contém, mas o arquivo B não? Eu poderia criar um script simples com, por exemplo, perl, mas se algo assim já existir, economizarei meu tempo a partir de agora.
Existe alguma ferramenta que possa obter linhas que o arquivo A contém, mas o arquivo B não? Eu poderia criar um script simples com, por exemplo, perl, mas se algo assim já existir, economizarei meu tempo a partir de agora.
Respostas:
Sim. A grep
ferramenta padrão para procurar arquivos por cadeias de texto pode ser usada para subtrair todas as linhas de um arquivo de outro.
grep -F -x -v -f fileB fileA
Isso funciona usando cada linha no arquivo B como um padrão ( -f fileB
) e tratando-a como uma sequência simples para corresponder (não uma expressão regular regular) ( -F
). Você força a correspondência a ocorrer em toda a linha ( -x
) e imprime apenas as linhas que não correspondem ( -v
). Portanto, você está imprimindo as linhas no arquivo A que não contêm os mesmos dados que qualquer linha no arquivoB.
A desvantagem desta solução é que ela não leva em consideração a ordem das linhas e, se sua entrada tiver linhas duplicadas em locais diferentes, você poderá não obter o que espera. A solução para isso é usar uma ferramenta de comparação real, como diff
. Você pode fazer isso criando um arquivo diff com o valor de contexto em 100% das linhas no arquivo e analisando-o apenas para as linhas que seriam removidas se converter o arquivo A para o arquivo B. (Observe que este comando também remove o diff depois de obter as linhas corretas.)
diff -U $(wc -l < fileA) fileA fileB | sed -n 's/^-//p' > fileC
-u
verdade, o argumento em minúsculas aceita um parâmetro de um número, desde que não seja seguido por um espaço. A vantagem da maneira que eu tinha antes é que ele funcionará com ou sem um valor, para que você possa usar algo nessa rotina de subcomando que não retornou saída. A letra maiúscula '-U', por outro lado, requer um argumento.
diff
pipeline funciona muito bem, obrigado.
grep
conforme necessário. Exemplo:grep -F -x -v -f <(sort fileB) <(sort fileA)
diff
é que a posição no arquivo é levada em consideração.
A resposta depende muito do tipo e formato dos arquivos que você está comparando.
Se os arquivos que você está comparando são arquivos de texto classificados, a ferramenta GNU escrita por Richard Stallman e Davide McKenzie chamada comm
pode executar a filtragem que você procura. Faz parte dos coreutils.
Digamos que você tenha os 2 arquivos a seguir:
$ cat a
1
2
3
4
5
$ cat b
1
2
3
4
5
6
Linhas no arquivo b
que não estão no arquivo a
:
$ comm <(sort a) <(sort b) -3
6
comm
; infelizmente, comm
requer arquivos classificados
<()
? Funciona e eu entendo, mas existe um nome para essa estranheza?
<()
também é conhecido como substituição de processo .
comm
foi originalmente escrito por volta de 1973 por alguém do Bell Labs, não por rms. Você está se referindo à implementação do GNU, que veio muito depois. Houve muitas implementações diferentes dos utilitários Unix ao longo dos anos.
de stackoverflow ...
comm -23 arquivo1 arquivo2
-23 suprime as linhas que estão nos dois arquivos ou apenas no arquivo 2. Os arquivos precisam ser classificados (eles estão no seu exemplo), mas, se não estiverem, direcione-os primeiro pela classificação ...
Veja a página de manual aqui
Os métodos grep e comm (com classificação) demoram muito tempo em arquivos grandes. O SiegeX e o ghostdog74 compartilharam dois ótimos métodos awk para extrair linhas exclusivas para um dos dois arquivos no Stack Overflow:
$ awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2
$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
Se os arquivos forem grandes e você não tiver um pedido personalizado para suas entradas, o grep levará muito tempo. Uma alternativa rápida seria
sort file1 > 1
sort file2 > 2
diff 1 2 | grep "\>" | sed -e 's/> //'
[arquivo2-arquivo1 resulta em tela, canal para arquivo etc.]
Mudar >
para <
obteria a subtração oposta.rm 1 2