O script produz qualquer linha no segundo arquivo que não ocorra no primeiro arquivo.
awk 'NR==FNR { b[$0] = 1; next } !b[$0]' 1.txt 2.txt
O script awk
começa com a comparação de NR
a FNR
. NR
é o número de registros (linhas) lidos até o momento, no total, incluindo o registro atual. FNR
é o número de registros lidos no arquivo de entrada atual . Se esses dois números forem iguais, ainda estaremos lendo o arquivo de entrada primeiro . Observe que isso quebrará se o primeiro arquivo por acaso é vazio , pois NR == FNR
seria verdadeiro para o segundo arquivo também.
Se estivermos lendo o primeiro dos arquivos de entrada (assumiremos que não está vazio), b[$0] = 1
usará o conteúdo do registro atual como uma chave de hash e armazenará o valor 1 para essa chave no array b
(índices de array podem ser strings em awk
). O script então executa next
, o que significa que ele volta para o início do script e lê o próximo registro.
Se NR
for não igual a FNR
, isso significa que estamos lendo o segundo dos dois arquivos de entrada, e !b[$0]
é um teste com o registro de entrada atual (linha) como uma chave na matriz b
que foi preenchida anteriormente. Se um 1 for armazenado para o registro atual em b
, saberemos que isso foi encontrado anteriormente no primeiro arquivo. O !
nega o teste.
Se o teste for verdadeiro, ou seja, se a linha atual do segundo arquivo não foi vista anteriormente no primeiro arquivo, a ação padrão será executada. A ação padrão para um teste sem o bloco {...}
correspondente é a saída da linha atual (ou seja, ele age como se o código fosse !b[$0] { print }
).
Como esse script awk
lê todas as linhas (exclusivas) do primeiro arquivo na memória, pode não ser uma boa ideia rodar em arquivos muito grandes.
Nesses casos, pode ser melhor fazer algo como
comm -13 <( sort -u file1 ) <( sort -u file2 )
(que requer um shell que saiba sobre substituições de processos), ou apenas
comm -13 file1 file2
se os arquivos já estiverem classificados.
Isso não gera a mesma saída exata como o script awk
gerará qualquer linha de file2
que ocorrer várias vezes uma vez para cada vez que ocorrer, enquanto o comando comm
acima não se sort -u
for usado na entrada.
Veja o manual comm
em seu sistema para mais informações.
Enfrentando as perguntas nos comentários:
- Sim,
FNR
é o número de registros lidos no arquivo de entrada atual. -
NR
eFNR
não "pertencem" a nenhum arquivo, eles são apenas contadores. O contadorFNR
é redefinido quando o final de um arquivo é atingido. - Tanto
NR
comoFNR
são incrementados ao ler uma linha de um arquivo. O comandonext
força um salto para o início do script, o que também faz com que a próxima linha seja lida.NR
eFNR
são incrementados por esta ação desde que uma nova linha é lida. - Se
NR != FNR
, isso significa que passamos do primeiro arquivo.FNR
foi redefinido para zero ao atingir o final do arquivo anterior, masNR
continua contando. -
$0
é uma variável que mantém a linha atual. Ele contém a linha completa como lida no arquivo.$1
,$2
etc. contém os campos da linha atual como divididos no valor da variávelIFS
(geralmente qualquer espaço em branco). Se a linha atual forhello world
, então$0
terá o valorhello world
, enquanto$1
terá o valorhello
e$2
terá o valorworld
(desde que a linha foi dividida no espaço) . Este script usa apenas$0
e você pode pensar em$0
como "o conteúdo da linha de entrada atual". -
b[$0] = 1
é uma atribuição de um valor para um determinado local / índice na matrizb
. A localização é determinada pela linha atual,$0
, e o valor atribuído é 1. Isso faz com que a matrizb
aja como uma "tabela de consulta"; seb[i]
for 1 para qualquer índice em particulari
, isso significa que foi visto no primeiro arquivo de entrada. -
!b[$0]
será verdadeiro se o valor armazenado no índice$0
deb
for zero (ou se não for inicializado), ou seja, seb[$0]
nunca recebeu o valor 1, ou seja, a linha que acabou de ler do segundo arquivo foi não visto anteriormente no primeiro arquivo. Como não há nenhuma ação (nenhum bloco{...}
) correspondente a este teste, a ação padrão de impressão$0
é executada. Isso tem o efeito de imprimir todas as linhas do segundo arquivo que não está presente no primeiro arquivo.