diff dentro de una línea


113

Tengo algunos volcados de sql en los que estoy mirando las diferencias. diffobviamente puede mostrarme la diferencia entre dos líneas, pero me estoy volviendo loco tratando de encontrar qué valores en la larga lista de valores separados por comas son en realidad los que hacen que las líneas sean diferentes.

¿Qué herramienta puedo usar para señalar las diferencias exactas de caracteres entre dos líneas en ciertos archivos?


Respuestas:


93

Hay wdiff , la palabra diff para eso.

En el escritorio, meld puede resaltar las diferencias dentro de una línea para usted.


8
Color wdiff:wdiff -w "$(tput bold;tput setaf 1)" -x "$(tput sgr0)" -y "$(tput bold;tput setaf 2)" -z "$(tput sgr0)" file1 file2
l0b0

47
Para el color, instale colordiff , luego haga:wdiff a b | colordiff
philfreo

Meld en realidad es extremadamente lento (minutos) para mostrar las diferencias intralíneas entre los archivos basados ​​en líneas.
Dan Dascalescu

También hay una dwdiffherramienta que es en su mayoría compatible wdiffpero que también admite salida en color y probablemente algunas otras características. Y está más disponible en algunas distribuciones de Linux como Arch.
MarSoft

44
wdiff -n a b | colordiff, aconseja man colordiff.
Camille Goudeseune

25

Solo otro método usando git-diff:

git diff -U0 --word-diff --no-index -- foo bar | grep -v ^@@

grep -v si no está interesado en las posiciones de las diferencias.


2
Este es exactamente el comportamiento que estaba tratando de imitar: no me di cuenta de que podía usar git-diff sin indexar uno de los archivos.
spinup

1
--word-diff es la opción clave aquí. ¡Gracias!
user2707671

1
--no-index solo es necesario si estás en un directorio de trabajo de git y tanto foo como bar también lo están.
xn.

22

Lo he usado vimdiffpara esto.

Aquí hay una captura de pantalla (no la mía) que muestra diferencias menores de uno o dos caracteres que se destacan bastante bien. Un tutorial rápido también .


En mi caso, no pude ver la diferencia, así que abrí los archivos en gvim -d f1 f2, las líneas largas particulares se resaltaron como diferentes, sin embargo, la diferencia real se destacó en rojo extra
zzapper

He estado usando vim por siempre, ¡pero no tenía idea sobre vimdiff!
mitchus

Y hay diffchar.vim para diferencias de nivel de personaje.

2
Por mucho que amo vim y vimdiff, el algoritmo de vimdiff para resaltar diferencias en una línea es bastante básico. Parece que simplemente elimina el prefijo y el sufijo comunes, y resalta todo entre diferentes. Esto funciona si todos los caracteres que cambiaron se agrupan, pero si se extienden no funciona bien. También es terrible para el texto envuelto en palabras.
Laurence Gonsalves

Para líneas largas como en el OP vimdiff -c 'set wrap' -c 'wincmd w' -c 'set wrap' a b, sugiere stackoverflow.com/a/45333535/2097284 .
Camille Goudeseune

6

Aquí hay un método "... el pelo del perro que te mordió" ...
diffte llevó a este punto; úsalo para llevarte más lejos ...

Aquí está la salida del uso de los pares de líneas de muestra ... indica una TAB

Paris in the     spring 
Paris in the the spring 
             vvvv      ^

A ca t on a hot tin roof.
a cant on a hot  in roof 
║   v           ^       ^

the quikc brown box jupps ober the laze dogs 
The☻qui ckbrown fox jumps over the lazy dogs 
║  ║   ^ ║      ║     ║    ║          ║     ^

Aquí está el guión ... Solo necesita descubrir los pares de líneas de alguna manera ... (He usado diff solo una vez (¿dos veces?) Antes de hoy, así que no sé sus muchas opciones, y ordenar las opciones para esto el guión fue suficiente para mí, por un día :) .. Creo que debe ser lo suficientemente simple, pero me espera un descanso para tomar café ...

#
# Name: hair-of-the-diff
# Note: This script hasn't been extensively tested, so beware the alpha bug :) 
#   
# Brief: Uses 'diff' to identify the differences between two lines of text
#        $1 is a filename of a file which contains line pairs to be processed
#
#        If $1 is null "", then the sample pairs are processed (see below: Paris in the spring 
#          
# ║ = changed character
# ^ = exists if first line, but not in second 
# v = exists if second line, but not in first

bname="$(basename "$0")"
workd="/tmp/$USER/$bname"; [[ ! -d "$workd" ]] && mkdir -p "$workd"

# Use $1 as the input file-name, else use this Test-data
# Note: this test loop expands \t \n etc ...(my editor auto converts \t to spaces) 
if [[ "$1" == '' ]] ;then
  ifile="$workd/ifile"
{ while IFS= read -r line ;do echo -e "$line" ;done <<EOF
Paris in the spring 
Paris in the the spring
A cat on a hot tin roof.
a cant on a hot in roof
the quikc brown box jupps ober the laze dogs 
The\tquickbrown fox jumps over the lazy dogs
EOF
} >"$ifile"
else
  ifile="$1"
fi
#
[[ -f "$ifile" ]] || { echo "ERROR: Input file NOT found:" ;echo "$ifile" ;exit 1 ; }
#  
# Check for balanced pairs of lines
ilct=$(<"$ifile" wc -l)
((ilct%2==0)) || { echo "ERROR: Uneven number of lines ($ilct) in the input." ;exit 2 ; }
#
ifs="$IFS" ;IFS=$'\n' ;set -f
ix=0 ;left=0 ;right=1
while IFS= read -r line ;do
  pair[ix]="$line" ;((ix++))
  if ((ix%2==0)) ;then
    # Change \x20 to \x02 to simplify parsing diff's output,
    #+   then change \x02 back to \x20 for the final output. 
    # Change \x09 to \x01 to simplify parsing diff's output, 
    #+   then change \x01 into ☻ U+263B (BLACK SMILING FACE) 
    #+   to the keep the final display columns in line. 
    #+   '☻' is hopefully unique and obvious enough (otherwise change it) 
    diff --text -yt -W 19  \
         <(echo "${pair[0]}" |sed -e "s/\x09/\x01/g" -e "s/\x20/\x02/g" -e "s/\(.\)/\1\n/g") \
         <(echo "${pair[1]}" |sed -e "s/\x09/\x01/g" -e "s/\x20/\x02/g" -e "s/\(.\)/\1\n/g") \
     |sed -e "s/\x01/☻/g" -e "s/\x02/ /g" \
     |sed -e "s/^\(.\) *\x3C$/\1 \x3C  /g" \
     |sed -n "s/\(.\) *\(.\) \(.\)$/\1\2\3/p" \
     >"$workd/out"
     # (gedit "$workd/out" &)
     <"$workd/out" sed -e "s/^\(.\)..$/\1/" |tr -d '\n' ;echo
     <"$workd/out" sed -e "s/^..\(.\)$/\1/" |tr -d '\n' ;echo
     <"$workd/out" sed -e "s/^.\(.\).$/\1/" -e "s/|/║/" -e "s/</^/" -e "s/>/v/" |tr -d '\n' ;echo
    echo
    ((ix=0))
  fi
done <"$ifile"
IFS="$ifs" ;set +f
exit
#

4

wdiffen realidad es un método muy antiguo para comparar archivos palabra por palabra. Funcionó reformateando archivos, luego usándolos diffpara encontrar diferencias y volviéndolos a pasar. Yo mismo sugerí agregar contexto, para que, en lugar de comparar palabra por palabra, lo haga con cada palabra rodeada de otras palabras de 'contexto'. Eso permite que el diff se sincronice mucho mejor en pasajes comunes en archivos, especialmente cuando los archivos son en su mayoría diferentes con solo unos pocos bloques de palabras comunes. Por ejemplo, al comparar texto para plagio o reutilización.

dwdifffue creado más tarde a partir de wdiff. Pero dwdiff utiliza esa función de reformateo de texto con buenos resultados dwfilter. Este es un gran desarrollo: significa que puede volver a formatear un texto para que coincida con otro, y luego compararlos con cualquier visualizador gráfico de línea por línea. Por ejemplo, usándolo con diff gráfico "difuso" ...

dwfilter file1 file2 diffuse -w

Esto reformatea file1el formato file2y lo da diffusepara una comparación visual. file2no está modificado, por lo que puede editar y combinar diferencias de palabras directamente en él diffuse. Si desea editar file1, puede agregar -rpara revertir qué archivo se reformatea. ¡Pruébalo y encontrarás que es extremadamente poderoso!

Mi preferencia por la diferencia gráfica (que se muestra arriba) es diffuseque se siente mucho más limpia y útil. También es un programa independiente de Python, lo que significa que es fácil de instalar y distribuir a otros sistemas UNIX.

Otras diferencias gráficas parecen tener muchas dependencias, pero también se pueden usar (usted elige). Estos incluyen kdiff3o xxdiff.


4

Utilizando la solución de @ Peter.O como base, la reescribí para hacer una serie de cambios.

ingrese la descripción de la imagen aquí

  • Solo imprime cada línea una vez, usando color para mostrarle las diferencias.
  • No escribe ningún archivo temporal, sino que lo canaliza todo.
  • Puede proporcionar dos nombres de archivo y comparará las líneas correspondientes en cada archivo. ./hairOfTheDiff.sh file1.txt file2.txt
  • De lo contrario, si usa el formato original (un solo archivo con cada segunda línea que necesita ser comparado con el anterior) ahora puede simplemente conectarlo, no es necesario que exista ningún archivo para ser leído. Echa un vistazo a demola fuente; esto puede abrir la puerta a una tubería elegante para no necesitar archivos para dos entradas separadas también, usando pastemúltiples descriptores de archivos.

Sin resaltar significa que el personaje estaba en ambas líneas, resaltar significa que estaba en el primero y rojo significa que estaba en el segundo.

Los colores se pueden cambiar a través de variables en la parte superior de la secuencia de comandos e incluso puede renunciar a los colores por completo mediante el uso de caracteres normales para expresar diferencias.

#!/bin/bash

same='-' #unchanged
up='△' #exists in first line, but not in second 
down='▽' #exists in second line, but not in first
reset=''

reset=$'\e[0m'
same=$reset
up=$reset$'\e[1m\e[7m'
down=$reset$'\e[1m\e[7m\e[31m'

timeout=1


if [[ "$1" != '' ]]
then
    paste -d'\n' "$1" "$2" | "$0"
    exit
fi

function demo {
    "$0" <<EOF
Paris in the spring 
Paris in the the spring
A cat on a hot tin roof.
a cant on a hot in roof
the quikc brown box jupps ober the laze dogs 
The quickbrown fox jumps over the lazy dogs
EOF
}

# Change \x20 to \x02 to simplify parsing diff's output,
#+   then change \x02 back to \x20 for the final output. 
# Change \x09 to \x01 to simplify parsing diff's output, 
#+   then change \x01 into → U+1F143 (Squared Latin Capital Letter T)
function input {
    sed \
        -e "s/\x09/\x01/g" \
        -e "s/\x20/\x02/g" \
        -e "s/\(.\)/\1\n/g"
}
function output {
    sed -n \
        -e "s/\x01/→/g" \
        -e "s/\x02/ /g" \
        -e "s/^\(.\) *\x3C$/\1 \x3C  /g" \
        -e "s/\(.\) *\(.\) \(.\)$/\1\2\3/p"
}

ifs="$IFS"
IFS=$'\n'
demo=true

while IFS= read -t "$timeout" -r a
do
    demo=false
    IFS= read -t "$timeout" -r b
    if [[ $? -ne 0 ]]
    then
        echo 'No corresponding line to compare with' > /dev/stderr
        exit 1
    fi

    diff --text -yt -W 19  \
        <(echo "$a" | input) \
        <(echo "$b" | input) \
    | \
    output | \
    {
        type=''
        buf=''
        while read -r line
        do
            if [[ "${line:1:1}" != "$type" ]]
            then
                if [[ "$type" = '|' ]]
                then
                    type='>'
                    echo -n "$down$buf"
                    buf=''
                fi

                if [[ "${line:1:1}" != "$type" ]]
                then
                    type="${line:1:1}"

                    echo -n "$type" \
                        | sed \
                            -e "s/[<|]/$up/" \
                            -e "s/>/$down/" \
                            -e "s/ /$same/"
                fi
            fi

            case "$type" in
            '|')
                buf="$buf${line:2:1}"
                echo -n "${line:0:1}"
                ;;
            '>')
                echo -n "${line:2:1}"
                ;;
            *)
                echo -n "${line:0:1}"
                ;;
            esac
        done

        if [[ "$type" = '|' ]]
        then
            echo -n "$down$buf"
        fi
    }

    echo -e "$reset"
done

IFS="$ifs"

if $demo
then
    demo
fi

3

Aquí hay una línea simple:

diff -y <(cat a.txt | sed -e 's/,/\n/g') <(cat b.txt | sed -e 's/,/\n/g')

La idea es reemplazar las comas (o el delimitador que desee usar) con líneas nuevas usando sed. diffluego se encarga del resto.


2
  • xxdiff: Otra herramienta es xxdiff (GUI), que primero debe instalarse.
  • hoja de cálculo: para los datos de la base de datos, .csvse puede crear fácilmente una hoja de cálculo , y se (A7==K7) ? "" : "diff"inserta una fórmula o similar y se pega y copia.

1
xxdiff se parece a los años 80. Meld se ve mucho mejor, pero es extremadamente lento para archivos similares a CSV. He encontrado que Diffuse es la herramienta de diferencia de Linux más rápida.
Dan Dascalescu

@DanDascalescu: una herramienta que hace el trabajo siempre se ve bien, sin importar la antigüedad que tenga. Otro, que usé ocasionalmente, pero no está instalado para probarlo con datos largos de columna, es tkdiff .
usuario desconocido

¿Xxdiff muestra líneas movidas ? ¿O simplemente muestra una línea faltante en un archivo y una agregada en el otro? (Intenté construir xxdiff pero qmake falló y veo que no se molestan en publicar un paquete Debian).
Dan Dascalescu

@DanDascalescu: Hoy, solo tengo tkdiff instalado.
usuario desconocido

1

En la línea de comando, me aseguraría de agregar nuevas líneas juiciosas antes de comparar archivos. Puede usar sed, awk, perl o cualquier cosa realmente para agregar saltos de línea de alguna manera sistemática; sin embargo, asegúrese de no agregar demasiados.

Pero creo que lo mejor es usar vim, ya que resalta las diferencias de palabras. vim es bueno si no hay demasiadas diferencias y las diferencias son simples.


Aunque no es realmente una respuesta a la pregunta, esta técnica es bastante eficiente para aprender sobre pequeñas diferencias en las largas colas.
jknappen

1

kdiff3 se está convirtiendo en el visor de diferencias de GUI estándar en Linux. Es similar a xxdiff , pero creo que kdiff3 es mejor. Hace muchas cosas bien, incluida su solicitud para mostrar "diferencias exactas de caracteres entre dos líneas en ciertos archivos".


KDiff3 es extremadamente lento para resaltar las diferencias en línea en los archivos CSV. No lo recomendaría
Dan Dascalescu

1

Si estoy leyendo tu pregunta correctamente, la uso diff -ypara este tipo de cosas.

Hace que comparar una comparación lado a lado sea mucho más simple para encontrar qué líneas arrojan las diferencias.


1
Esto no resalta la diferencia dentro de la línea. Si tienes una larga fila, es doloroso ver la diferencia. wdiff, git diff --word-diff, vimgit, meld, kbdiff3, tkdiff todos hacen esto.
user2707671

1

Tuve el mismo problema y lo resolví con PHP Fine Diff , una herramienta en línea que le permite especificar granularidad. Sé que técnicamente no es una herramienta * nix, pero realmente no quería descargar un programa solo para hacer una diferencia única de nivel de personaje.


Algunos usuarios no pueden cargar archivos confidenciales o grandes en una herramienta en línea aleatoria. Hay muchas herramientas que muestran diferencias de nivel de línea sin comprometer su privacidad.
Dan Dascalescu

Sí hay. Pero para los diferenciales que no contienen información confidencial, las herramientas en línea pueden ser una buena solución.
pillravi

Las herramientas de diferencias en línea tampoco admiten la integración de línea de comandos. No puede usarlos desde su flujo de control de versiones. También son mucho más engorrosos de usar (seleccione el archivo 1, seleccione el archivo 2, subir) y no pueden fusionarse.
Dan Dascalescu
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.