Como remover padrões específicos de correspondência de texto de um arquivo

4

Eu quero comparar dois arquivos para verificar as diferenças entre eles, fileA e fileB. fileA é como um arquivo de template e fileB é o arquivo que eu quero comparar com ele. Toda vez que eu encontrar uma diferença eu quero a saída dessa diferença para fileC.

A dificuldade é que fileA e fileB contêm certas (não todas) linhas que possuem alguns dados que sempre serão diferentes - hora, data e um código de id gerado aleatoriamente. No entanto, eu não quero saídas de linhas para fileC onde a única diferença é hora, data e código id.

Então, o que eu gostaria de fazer é remover a hora, a data e o código id de qualquer linha onde eles ocorram no fileB (eu posso fazer isso manualmente no fileA) e então fazer uma comparação com o fileB, gerando as diferentes linhas fileC.

Observe que o texto a ser removido sempre segue padrões específicos. Então eu posso encontrar o texto usando o grep com esses padrões, mas não sei como removê-lo ...

Aqui está um exemplo dos dois arquivos para mostrar o que quero dizer:

  • fileB

    qaqa rara
    abc 10:12:25 08/20/2014 123456 def
    ghi fff ddd
    jkl 09:20:40 08/20/2014 978645 dfdf gggg
    
  • fileA

    qaqa rara
    abc 10:32:15 07/15/2014 121456 xxx
    ghi eee ddd
    jkl 10:01:22 07/15/2014 971645 dfdf gggg
    

Eu quero encontrar a diferença entre os dois arquivos acima, desconsiderando o tempo (por exemplo, 10:12:25), data (por exemplo, 20/08/2014) ou código id (por exemplo, 123456) e a saída das diferenças para fileC

As duas linhas que são diferentes, portanto, são as linhas 2 e 3. A linha 1 é a mesma para os dois arquivos. A linha 4 é a mesma para os dois arquivos quando as informações de hora, data e identificação foram removidas.

    
por didjek 20.08.2014 / 12:14

4 respostas

2

Se seus timestamps forem formatados de forma consistente, você poderá retirá-los (com sed, por exemplo) antes de processar os arquivos com qualquer método de diferenciação, por exemplo

diff <(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileA) <(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileB)

Teste em seus arquivos de entrada fornecidos:

$ diff \
<(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileA) \
<(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileB)
2,3c2,3
< abc xxx
< ghi eee ddd
---
> abc def
> ghi fff ddd
    
por 20.08.2014 / 14:13
1
diff \
<(sed -r 's\[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{6} \' fileA) \
<(sed -r 's\[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{6} \' fileB) \
| egrep '^> ' | sed -r 's/^> //' > fileC

Explicação

Descarte as partes irrelevantes conforme fornecidas na pergunta do OP a partir do arquivo A e do arquivo B e insira isso no diff.

diff produzirá as seções alteradas com um ">" precedente, então ignore todo o resto, exceto as alterações.

Finalmente, retire o primeiro ">" da saída e armazene-o no fileC conforme a pergunta.

Eu originalmente fiz isso de forma ligeiramente diferente, mas eu notei que os arquivos podem variar na seção irrelevante, então ele precisa ser pré-removido, não post-stripped, mais, diff resultaria em informações que não foram alteradas ao considerar as partes relevantes somente.

Dado o exemplo de entrada, cat fileC dá:

abc def
ghi fff ddd

O comando sed está procurando pela expressão regular fornecida, que descreve os dados irrelevantes, e substituindo-a por uma string vazia - isto é, exclui-a.

    
por 20.08.2014 / 13:19
0

O comando mais fácil para obter resultado seria o abaixo

$ diff <(tr -s "[0-9],:,/" " " < fileA) <(tr -s "[0-9],:,/" " " < fileB)

O comando é muito direto e não há expressão regular complexa também.

A saída da amostra será como abaixo

2,3c2,3
< abc xxx
< ghi eee ddd
---
> abc def
> ghi fff ddd

Espero que seja isso que você quer.

    
por 20.08.2014 / 15:32
0
{   paste -d\| /dev/fd/3 /dev/fd/4 |
    sed '/\([^ ]*\) [0-9:/ ]*\(.*\)| .*/d;=' |
    sed 'N;s/\(\n\)\(.*\)|/:\tFILEA: \tFILEB: /'
} 3<<\FILEA 4<<\FILEB
qaqa rara
abc 10:12:25 08/20/2014 123456 def
ghi fff ddd
jkl 09:20:40 08/20/2014 978645 dfdf gggg
FILEA
qaqa rara
abc 10:32:15 07/15/2014 121456 xxx
ghi eee ddd
jkl 10:01:22 07/15/2014 971645 dfdf gggg
FILEB

OUTPUT

2:      FILEA: abc 10:12:25 08/20/2014 123456 def
        FILEB: abc 10:32:15 07/15/2014 121456 xxx
3:      FILEA: ghi fff ddd
        FILEB: ghi eee ddd

Você não precisa se livrar da hora e da data - eles não são um grande obstáculo, desde que os personagens que os compõem sejam confiáveis.

No pipeline acima, paste primeiro anexa a linha correspondente de FILEB à final de cada linha de FILEA com um único separador | e, em seguida, imprime os resultados em stdout .

sed pega o fluxo e compara:

  • a primeira sequência de 0 ou mais caracteres que não são espaço (referenciados como )

  • todos os caracteres que ocorrem entre as seguintes seqüências: (referenciada como )

    • pelo menos um único caractere <space> , em seguida, 0 ou mais de qualquer um dos itens a seguir:

    • <space> caracteres

    • <digit> caracteres

    • <:colon> caracteres

    • </slash> caracteres

  • até, mas não incluindo o último caractere | na linha

... com |.* . Se eles corresponderem a sed , exclui a linha. Se não, imprime a linha precedida pelo seu número de linha.

O processo final sed apenas prepara a saída (espero) .

    
por 20.08.2014 / 16:43