Como diferenciar arquivos ignorando comentários (linhas iniciando com #)?

47

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

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

  • espaço em branco líder opcional (guias e espaços)
  • sinal de hash ( # )
  • qualquer outro caractere

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

Como sugerido em o sabor de regex da ferramenta "diff" parece faltando? , eu tentei grep -G :

grep -G '#.*' file

Isto parece corresponder aos comentários, mas não funciona para diff -I '#.*' file1 file2 .

Então, como essa opção deve ser usada? Como posso fazer com que diff pule algumas linhas (no meu caso, comentários)? Por favor, não sugira grep ing o arquivo e compare os arquivos temporários.

    
por Lekensteyn 20.07.2011 / 15:05

5 respostas

39

De acordo com Gilles, a opção -I apenas ignora uma linha se nada mais dentro desse conjunto coincidir com a correspondência de -I . Eu não entendi totalmente até que eu testei.

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 não há resposta até agora explicando como usar a opção -I corretamente, fornecerei uma alternativa que funcione em shells 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 processos que abre um descritor de arquivo para o comando, este elimina a necessidade de um arquivo temporário
  • grep - comando para imprimir linhas (não) que correspondem a um padrão
    • -v - mostra linhas não correspondentes
    • E - use expressões regulares estendidas
    • '^\s*(#|$)' - uma expressão regular que corresponde a comentários e linhas vazias
      • ^ - corresponde ao começo de uma linha
      • \s* - corresponde ao espaço em branco (tabulações e espaços) se houver
      • (#|$) corresponde a uma marca de hash ou, alternativamente, ao final de uma linha
por 20.07.2011 / 15:49
5

Tente:

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

Por favor, note que o regex tem que coincidir com a linha correspondente em ambos os arquivos e corresponde a cada linha alterada no pedaço, a fim de trabalhar, caso contrário, ele ainda mostrará a diferença.

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

Podemos ler o manual diffutils :

However, -I only ignores the insertion or deletion of lines that contain the regular expression if every changed line in the hunk (every insertion and every deletion) matches the regular expression.

In other words, for each non-ignorable change, diff prints the complete set of changes in its vicinity, including the ignorable ones. You can specify more than one regular expression for lines to ignore by using more than one -I option. diff tries to match each line against each regular expression, starting with the last one given.

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

    
por 21.03.2015 / 23:20
1

Eu geralmente ignoro essa confusão:

  • Gerando versões não comentadas usando grep -v "^#" | cat -s e diferenciando essas ou ...
  • Usando vim -d para ver os arquivos. O destaque da sintaxe cuida de tornar as diferenças entre comentários e não comentários bastante óbvias. O destaque do diferencial in-line para que você possa ver quais valores ou partes dos valores foram alterados de relance torna isso meu favorito.
por 20.07.2011 / 15:11
1

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

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

Por isso, 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 patch.

No entanto, se uma nova linha for adicionada em vez da linha de comentário ... o comentário produzirá um Hunk FAILED ao corrigir 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 \)  <(./outcom.sed \)" > 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 & / dev / fd / 63 são arquivos produzidos por substituição de processos. A 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 virá quando aplicarmos 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 \)  <(./outcom.sed \)" > 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 arquivo de trabalho do arquivo de correção (sem garantia de resultado em um processo de comparação muito complexo).

    
por 23.08.2014 / 10:24
0

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

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

ou você pode fazer

sed -e '/^#.*/d' -e 's/#.*//g' | cat -s
    
por 20.07.2011 / 15:38