¿Cómo diferenciar archivos ignorando comentarios (líneas que comienzan con #)?

55

Tengo dos archivos de configuración, el original del administrador de paquetes y uno personalizado modificado por mí mismo. He agregado algunos comentarios para describir el comportamiento.

¿Cómo puedo ejecutar difflos archivos de configuración, omitiendo los comentarios? Una línea comentada se define por:

  • espacios en blanco iniciales opcionales (pestañas y espacios)
  • signo hash ( #)
  • cualquier otro personaje

Sería la expresión regular (más simple) omitiendo el primer requisito #.*. Probé la opción --ignore-matching-lines=RE( -I RE) de GNU diff 3.0, pero no pude hacerlo funcionar con ese RE. También lo intenté .*#.*y .*\#.*sin suerte. Poner literalmente la línea ( Port 631) como REno coincide con nada, tampoco ayuda a colocar el RE entre barras.

Como se sugiere en la herramienta "diff", ¿parece que falta la expresión regular de la herramienta? , Intenté grep -G:

grep -G '#.*' file

Esto parece coincidir con los comentarios, pero no funciona diff -I '#.*' file1 file2.

Entonces, ¿cómo debe usarse esta opción? ¿Cómo puedo hacer diffomitir ciertas líneas (en mi caso, comentarios)? No sugiera grepel archivo ni compare los archivos temporales.

Lekensteyn
fuente
12
La -Iopción hace que se ignore un bloque solo si todas sus líneas coinciden con la expresión regular. Por lo tanto, puede ignorar un cambio de solo comentario de esa manera, pero no los cambios de comentario que están cerca de un cambio sin comentario.
Gilles 'SO- deja de ser malvado'
@Gilles: Gracias, ahora entiendo por qué diff -Ino se comporta como esperaba. Actualicé mi respuesta con un ejemplo que me aclaró este comportamiento.
Lekensteyn

Respuestas:

49

Según Gilles, la -Iopción solo ignora una línea si nada más dentro de ese conjunto coincide con la coincidencia de -I. No lo entendí completamente hasta que lo probé.

La prueba

Hay tres archivos involucrados en mi prueba:
Archivo test1:

    text

Archivo test2:

    text
    #comment

Archivo test3:

    changed text
    #comment

Los 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

La forma alternativa

Como hasta ahora no hay una respuesta que explique cómo usar la -Iopción correctamente, proporcionaré una alternativa que funciona en bash shells:

diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
  • diff -u - diff unificado
    • -B - ignorar líneas en blanco
  • <(command)- una función bash llamada sustitución de proceso que abre un descriptor de archivo para el comando, esto elimina la necesidad de un archivo temporal
  • grep - comando para imprimir líneas (no) que coinciden con un patrón
    • -v - Mostrar líneas no coincidentes
    • E - usa expresiones regulares extendidas
    • '^\s*(#|$)' - una expresión regular que coincida con comentarios y líneas vacías
      • ^ - coincide con el comienzo de una línea
      • \s* - unir espacios en blanco (tabuladores y espacios) si los hay
      • (#|$) hacer coincidir una marca hash o, alternativamente, el final de una línea
Lekensteyn
fuente
6

Tratar:

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

Tenga en cuenta que la expresión regular tiene que coincidir con la línea correspondiente en ambos archivos y coincide con cada línea cambiada en el trozo para que funcione, de lo contrario, seguirá mostrando la diferencia.

Utilice comillas simples para proteger el patrón de la expansión del shell y para escapar de los caracteres reservados por expresiones regulares (por ejemplo, corchetes).

Podemos leer en el diffutilsmanual:

Sin embargo, -Isolo ignora la inserción o eliminación de líneas que contienen la expresión regular si cada línea cambiada en el trozo (cada inserción y cada eliminación) coincide con la expresión regular.

En otras palabras, para cada cambio no ignorable, diffimprime el conjunto completo de cambios en su vecindad, incluidos los ignorables. Puede especificar más de una expresión regular para que las líneas ignoren utilizando más de una -Iopción. diffintenta hacer coincidir cada línea con cada expresión regular, comenzando con la última dada.

Armel también explica bien este comportamiento aquí .

Relacionado: ¿Cómo puedo realizar una diferencia que ignore todos los comentarios?

kenorb
fuente
2

Después de buscar en la web, la forma alternativa de Lekensteyn es la mejor que encontré.

Pero quiero usar la salida dif como parche ... y hay un problema porque el número de línea se mantiene en nota debido a "grep -v".

Así que me propongo mejorar esta línea de comando:

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

No es perfecto, pero el número de línea se guarda en el archivo de parche.

Sin embargo, si se agrega una nueva línea en lugar de una línea de comentario ... el comentario generará un Fallo de Hunk al parchear, como podemos ver a continuación.

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

prueba ahora nuestro 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 & / dev / fd / 63 son archivos producidos por sustitución de procesos. La línea entre "+ nueva línea" y "-otro texto" es el carácter de espacio predeterminado definido en nuestra expresión sed para reemplazar los comentarios.

Y ahora, lo que viene cuando aplicamos este parche:

$ 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

La solución es no usar el formato diff unificado sin -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

ahora parchea el archivo de trabajo (sin garantía del resultado en un proceso diff muy complejo).

syjust
fuente
Su diferencia unificada no se aplica debido a las diferencias de contexto. Puede usar diff -U0 one twopara deshabilitar el contexto. Para parchear, hay un montón de herramientas que pueden ser más adecuadas, como kdiff3.
Lekensteyn el
Gracias por la -U0opción de deshabilitar el contexto. Nota: kdiff3 es una herramienta gráfica. Necesito una herramienta automática para administrar los atributos de git merge.
syjust
vimdiffadmite combinaciones de tres vías, puede valer la pena mirar.
Lekensteyn
Para ser más precisos, necesito una herramienta de script para automatizar el proceso de fusión de git con exludes en un script sql. kdiff3 y vimdiff son herramientas interactivas, no utilizables en mi caso.
syjust
1

Usualmente ignoro este desorden ya sea por:

  • Generando versiones no comentadas usando grep -v "^#" | cat -sy diferenciando esas o ...
  • Utilizando vim -dpara mirar los archivos. El resaltado de sintaxis se encarga de hacer que las diferencias entre comentarios y no comentarios sean bastante obvias. El resaltado de diferencias de la diferencia en línea para que pueda ver qué valores o partes de valores se han cambiado de un vistazo hace de este mi favorito.
Caleb
fuente
0

Esto es lo que uso para eliminar todas las líneas comentadas, incluso las que comienzan con una pestaña o espacio, y las en blanco:

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

o puedes hacer

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