Precisa entender o comando awk abaixo para localizar linhas ausentes em um arquivo

2

Encontrado abaixo do comando awk , que encontra linhas ausentes em 1.txt em comparação com 2.txt .

awk 'NR==FNR{b[$0]=1;next}!b[$0]' 1.txt 2.txt

Precisa entender passo a passo como essa awk construct encontra as linhas ausentes.

    
por Prashant BJ 17.09.2017 / 09:03

1 resposta

4

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:

  1. Sim, FNR é o número de registros lidos no arquivo de entrada atual.
  2. NR e FNR não "pertencem" a nenhum arquivo, eles são apenas contadores. O contador FNR é redefinido quando o final de um arquivo é atingido.
  3. Tanto NR como FNR são incrementados ao ler uma linha de um arquivo. O comando next força um salto para o início do script, o que também faz com que a próxima linha seja lida. NR e FNR são incrementados por esta ação desde que uma nova linha é lida.
  4. Se NR != FNR , isso significa que passamos do primeiro arquivo. FNR foi redefinido para zero ao atingir o final do arquivo anterior, mas NR continua contando.
  5. $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ável IFS (geralmente qualquer espaço em branco). Se a linha atual for hello world , então $0 terá o valor hello world , enquanto $1 terá o valor hello e $2 terá o valor world (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".
  6. b[$0] = 1 é uma atribuição de um valor para um determinado local / índice na matriz b . A localização é determinada pela linha atual, $0 , e o valor atribuído é 1. Isso faz com que a matriz b aja como uma "tabela de consulta"; se b[i] for 1 para qualquer índice em particular i , isso significa que foi visto no primeiro arquivo de entrada.
  7. !b[$0] será verdadeiro se o valor armazenado no índice $0 de b for zero (ou se não for inicializado), ou seja, se b[$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.
por 17.09.2017 / 09:21

Tags