Como diferenciar arquivos ignorando comentários (linhas começando com #)?

55

Eu tenho dois arquivos de configuração, o original do gerenciador de pacotes e o personalizado modificado por mim. Adicionei alguns comentários para descrever o comportamento.

Como posso executar diffnos arquivos de configuração, pulando os comentários? Uma linha comentada é definida por:

  • espaço em branco à esquerda opcional (guias e espaços)
  • sinal de hash ( #)
  • qualquer outro personagem

A expressão regular (mais simples) pulando o primeiro requisito seria #.*. Tentei a opção --ignore-matching-lines=RE( -I RE) do GNU diff 3.0, mas não consegui fazê-lo funcionar com esse RE. Eu também tentei .*#.*e .*\#.*sem sorte. Colocar literalmente a linha ( Port 631) como REnão corresponde a nada, nem ajuda a colocar o ER entre barras.

Como sugerido no sabor da regex da ferramenta “diff”, parece estar faltando? , Tentei grep -G:

grep -G '#.*' file

Isso parece coincidir com os comentários, mas não funciona diff -I '#.*' file1 file2.

Então, como essa opção deve ser usada? Como posso fazer diffpular certas linhas (no meu caso, comentários)? Por favor, não sugira grepo arquivo e compare os arquivos temporários.

Lekensteyn
fonte
12
A -Iopção faz com que um bloco seja ignorado apenas se todas as suas linhas corresponderem à regexp. Portanto, você pode ignorar uma alteração somente de comentário dessa maneira, mas não as alterações de comentários que estão próximas de uma alteração sem comentários.
Gilles 'SO- stop be evil'
@ Gilles: Obrigado, agora entendi porque diff -Inão se comporta como eu esperava. Atualizei minha resposta com um exemplo que esclareceu esse comportamento para mim.
Lekensteyn

Respostas:

49

De acordo com Gilles, a -Iopção ignora apenas uma linha se nada mais nesse conjunto corresponder, exceto a correspondência de -I. Não o compreendi completamente até o testar.

O teste

Três arquivos estão envolvidos no meu teste:
Arquivo test1:

    text

Arquivo test2:

    text
    #comment

Arquivo test3:

    changed text
    #comment

Os comandos:

$ # comparing files with comment-only changes
$ diff -u -I '#.*' test{1,2}
$ # comparing files with both comment and regular changes
$ diff -u -I '#.*' test{2,3}
--- test2       2011-07-20 16:38:59.717701430 +0200
+++ test3       2011-07-20 16:39:10.187701435 +0200
@@ -1,2 +1,2 @@
-text
+changed text
 #comment

O caminho alternativo

Como até o momento não há resposta para explicar como usar a -Iopção corretamente, fornecerei uma alternativa que funciona em shells do bash:

diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
  • diff -u - diff unificado
    • -B - ignorar linhas em branco
  • <(command)- um recurso bash chamado substituição de processo que abre um descritor de arquivo para o comando, eliminando a necessidade de um arquivo temporário
  • grep - comando para imprimir linhas (não) correspondentes a um padrão
    • -v - mostrar linhas não correspondentes
    • E - use expressões regulares estendidas
    • '^\s*(#|$)' - uma expressão regular combinando comentários e linhas vazias
      • ^ - corresponde ao início de uma linha
      • \s* - corresponder espaço em branco (tabulações e espaços), se houver
      • (#|$) corresponde a uma marca de hash ou, alternativamente, ao final de uma linha
Lekensteyn
fonte
6

Experimentar:

diff -b -I '^#' -I '^ #' file1 file2

Observe que o regex deve corresponder à linha correspondente nos dois arquivos e corresponde a todas as linhas alteradas no pedaço para funcionar, caso contrário, ainda mostrará a diferença.

Use aspas simples para proteger o padrão da expansão do shell e para escapar dos caracteres reservados à expressão regular (por exemplo, colchetes).

Podemos ler no diffutilsmanual:

No entanto, -Iapenas ignora a inserção ou exclusão de linhas que contêm a expressão regular se todas as linhas alteradas no pedaço (toda inserção e exclusão) corresponderem à expressão regular.

Em outras palavras, para cada mudança não ignorável, diffimprime o conjunto completo de mudanças nas proximidades, incluindo as ignoráveis. Você pode especificar mais de uma expressão regular para que as linhas sejam ignoradas usando mais de uma -Iopção. difftenta combinar cada linha com cada expressão regular, começando com a última.

Esse comportamento também é bem explicado pelo armel aqui .

Relacionado: Como posso executar uma comparação que ignora todos os comentários?

kenorb
fonte
2

Depois de pesquisar na web, a maneira alternativa de Lekensteyn é a melhor que encontrei.

Mas eu quero usar a saída dif como patch ... e há um problema porque o número da linha é mantido por nota por causa de "grep -v".

Então, pretendo melhorar esta linha de comando:

diff -u -B <(sed 's/^[[:blank:]]*#.*$/ /' file1)  <(sed 's/^[[:blank:]]*#.*$/ /' file2)

Não é perfeito, mas o número da linha é mantido no arquivo de correção.

No entanto, se uma nova linha for adicionada em vez da linha de comentário ... o comentário produzirá um Hunk FAILED ao aplicar patches, como podemos ver abaixo.

File test1:
  text
  #comment
  other text
File test2:
  text
  new line here
  #comment changed
  other text changed

teste agora nosso comando

$ echo -e "#!/usr/bin/sed -f\ns/^[[:blank:]]*#.*$/ /" > outcom.sed
$ echo "diff -u -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ chmod +x mydiff.sh outcom.sed
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
--- /dev/fd/63  2014-08-23 10:05:08.000000000 +0200
+++ /dev/fd/62  2014-08-23 10:05:08.000000000 +0200
@@ -1,2 +1,3 @@
 text
+new line

-other text
+other text changed

/ dev / fd / 62 e / dev / fd / 63 são arquivos produzidos por substituição de processo. Linha entre "+ nova linha" e "-outro texto" é o caractere de espaço padrão definido em nossa expressão sed para substituir comentários.

E agora, o que está chegando quando aplicamos este patch:

$ patch -p0 file1 < file.dif 
patching file file1
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file file1.rej

A solução é não usar o formato diff unificado sem -u

$ echo "diff -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
1a2
> new line
3c4
< other text
---
> other text changed
$ patch -p0 file1 < file.dif 
patching file file1
$ cat file1
text
new line
#comment
other text changed

agora remendo arquivo arquivo de trabalho (sem garantia de resultado em um processo diff muito complexo).

justo
fonte
Seu diff unificado falha na aplicação devido às diferenças de contexto. Você pode usar diff -U0 one twopara desativar o contexto. Para aplicar patches, existem várias ferramentas que podem ser mais adequadas, como o kdiff3.
Lekensteyn 23/08/14
Obrigado pela -U0opção de desativar o contexto. Nota: kdiff3 é uma ferramenta gráfica. Preciso de ferramenta automática para gerenciar atributos de mesclagem do git.
syjust 29/08/14
vimdiffsuporta fusões de três vias, pode valer a pena olhar.
Lekensteyn
para ser mais preciso, preciso de uma ferramenta de script para automatizar o processo de mesclagem do git com exclusões em um script sql. O kdiff3 e o vimdiff são ferramentas interativas, não utilizáveis ​​no meu caso.
syjust 29/08/14
1

Eu geralmente ignoro essa desordem por:

  • Gerando versões não comentadas usando grep -v "^#" | cat -se diferindo daquelas ou ...
  • Usando vim -dpara olhar para os arquivos. O destaque da sintaxe cuida de tornar as diferenças entre comentários e não comentários bastante óbvias. O realce diferencial da diferença em linha para que você possa ver rapidamente quais valores ou partes de valores foram alterados faz deste o meu favorito.
Caleb
fonte
0

Aqui está o que eu uso para remover todas as linhas comentadas - mesmo as que começam com uma guia ou espaço - e as em branco:

egrep -v "^$|^[[:space:]]*#" /path/to/file

ou você pode fazer

sed -e '/^#.*/d' -e 's/#.*//g' | cat -s
Philomath
fonte