diff dans une ligne


113

J'ai quelques draps SQL que je regarde les différences entre. diffJe peux évidemment me montrer la différence entre deux lignes, mais je me rend fou en essayant de trouver quelles valeurs de la longue liste de valeurs séparées par des virgules sont en réalité celles qui font que les lignes sont différentes.

Quel outil puis-je utiliser pour indiquer les différences de caractères exactes entre deux lignes dans certains fichiers?


Réponses:


93

Il y a wdiff , le mot-diff pour cela.

Sur le bureau, meld peut mettre en évidence les différences au sein d’une ligne.


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

47
Pour la couleur, installez colordiff , puis faites:wdiff a b | colordiff
philfreo le

La fusion est en fait extrêmement lente (quelques minutes) pour montrer les différences intra-lignes entre les fichiers basés sur les lignes.
Dan Dascalescu Le

Il existe également un dwdiffoutil compatible avec la plupart des applications en wdiffcouleur, mais également avec d’ autres fonctionnalités. Et il est plus disponible dans certaines distributions Linux comme Arch.
MarSoft

4
wdiff -n a b | colordiff, conseille man colordiff.
Camille Goudeseune

25

Juste une autre méthode utilisant git-diff:

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

grep -v si vous n'êtes pas intéressé par les positions des diffs.


2
C’est exactement le comportement que j’essayais d’imiter - je n’avais pas réalisé que je pouvais utiliser git-diff sans l’indexation d’un des fichiers.
spin

1
--word-diff est l'option clé ici. Merci!
user2707671

1
--no-index n'est requis que si vous êtes dans un répertoire de travail git et que foo et bar le sont aussi.
xn.

22

J'ai utilisé vimdiffpour cela.

Voici une capture d'écran (pas la mienne) montrant des différences mineures d'un ou deux caractères qui ressort assez bien. Un tutoriel rapide aussi .


Dans mon cas, je ne pouvais pas voir la différence, alors nous avons ouvert les fichiers dans gvim -d f1 f2. Les longues lignes ont été mises en évidence comme étant différentes, mais la différence réelle a été mise en évidence en rouge
zzapper

J'utilise vim depuis toujours, mais je n'avais aucune idée de vimdiff!
mitchus

Et il existe diffchar.vim pour les diffs au niveau du personnage.

2
Bien que j'aime vim et vimdiff, l'algorithme de vimdiff pour mettre en évidence les différences dans une ligne est assez basique. Il semble simplement supprimer le préfixe et le suffixe communs et mettre en évidence tout ce qui est différent. Cela fonctionne si tous les caractères qui ont changé sont regroupés, mais s'ils sont dispersés, cela ne fonctionne pas bien. C'est aussi terrible pour le texte recouvert de mots.
Laurence Gonsalves

Pour les lignes longues, comme dans le PO vimdiff -c 'set wrap' -c 'wincmd w' -c 'set wrap' a b, suggère stackoverflow.com/a/45333535/2097284 .
Camille Goudeseune

6

Voici une méthode ".. poil du chien qui vous a mordu" ...
diffvous en êtes à ce point; utilisez-le pour vous emmener plus loin ...

Voici le résultat de l'utilisation des paires de lignes de l'échantillon ... indique une tabulation

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 
║  ║   ^ ║      ║     ║    ║          ║     ^

Voici le script .. Vous avez juste besoin de fouiller les paires de lignes d'une manière ou d'une autre .. (Je n'ai utilisé diff qu'une fois (deux??) Avant aujourd'hui, donc je ne connais pas ses nombreuses options, et en trier les options pour cela le script me suffisait, pour un jour :) .. Je pense que ça doit être assez simple, mais je dois prendre une pause 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

wdiffest en fait une très ancienne méthode de comparaison de fichiers mot par mot. Cela fonctionnait en reformatant les fichiers, en utilisant ensuite diffpour trouver les différences et en les renvoyant. J'ai moi-même suggéré d'ajouter un contexte afin que, plutôt que de comparer mot par mot, il le fasse avec chaque mot entouré de mots «context». Cela permet au diff de se synchroniser beaucoup mieux sur les passages communs dans les fichiers, en particulier lorsque les fichiers sont très différents avec seulement quelques blocs de mots communs. Par exemple, lorsque vous comparez du texte pour un plagiat ou que vous le réutilisez.

dwdiffa été créé plus tard wdiff. Mais dwdiff utilise cette fonction de reformatage de texte à bon escient dans dwfilter. C'est un grand développement - cela signifie que vous pouvez reformater un texte pour en faire correspondre un autre, puis les comparer à l'aide de tout afficheur de diff graphique par ligne. Par exemple, en l'utilisant avec un diff graphique "diffus" ....

dwfilter file1 file2 diffuse -w

Cette reformate file1au format de file2et donne que diffusepour une comparaison visuelle. file2est non modifié, vous pouvez donc éditer et y fusionner les différences de mots directement dans diffuse. Si vous voulez éditer file1, vous pouvez ajouter -rpour inverser le fichier qui est reformaté. Essayez-le et vous constaterez qu'il est extrêmement puissant!

Ma préférence pour le diff graphique (illustré ci-dessus) est diffusede se sentir beaucoup plus propre et utile. En outre, il s'agit d'un programme python autonome, ce qui signifie qu'il est facile à installer et à distribuer sur d'autres systèmes UNIX.

D'autres différences graphiques semblent avoir beaucoup de dépendances, mais peuvent aussi être utilisées (à vous de choisir). Ceux-ci incluent kdiff3ou xxdiff.


4

En utilisant la solution de @ Peter.O comme base, je l'ai réécrite pour apporter un certain nombre de modifications.

entrez la description de l'image ici

  • Il n'imprime que chaque ligne une fois, en utilisant la couleur pour vous montrer les différences.
  • Il n'écrit aucun fichier temporaire, mais passe tout à la tuyauterie.
  • Vous pouvez fournir deux noms de fichier et comparer les lignes correspondantes dans chaque fichier. ./hairOfTheDiff.sh file1.txt file2.txt
  • Sinon, si vous utilisez le format d'origine (un fichier avec une ligne sur chaque seconde devant être comparée à celle qui précède), vous pouvez maintenant le transférer en un seul fichier, il ne doit plus exister de fichier pour être lu. Regardez demodans la source. cela peut ouvrir la porte à une tuyauterie sophistiquée afin de ne pas nécessiter de fichiers pour deux entrées distinctes également, à l'aide de pasteplusieurs descripteurs de fichiers.

Pas de surbrillance signifie que le personnage était dans les deux lignes, surligner signifie qu'il était dans la première, et le rouge signifie qu'il était dans la seconde.

Les couleurs sont modifiables par le biais de variables en haut du script et vous pouvez même renoncer entièrement à des couleurs en utilisant des caractères normaux pour exprimer des différences.

#!/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

Voici une simple ligne:

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

L'idée est de remplacer les virgules (ou le délimiteur que vous souhaitez utiliser) par newlines sed. diffprend ensuite soin du reste.


2
  • xxdiff: Un autre outil est xxdiff (GUI), qui doit être installé en premier.
  • feuille de calcul: pour les données de base de données, une feuille de calcul .csvest facile à créer, et une formule (A7==K7) ? "" : "diff"ou similaire insérée et copiée-collée.

1
xxdiff ressemble aux années 80. La fusion semble beaucoup mieux, mais elle est extrêmement lente pour les fichiers de type CSV. J'ai trouvé que Diffuse était l'outil de différenciation Linux le plus rapide.
Dan Dascalescu Le

@DanDascalescu: Un outil qui permet de faire le travail est toujours beau, peu importe son âge. Un autre, que j’ai utilisé occasionnellement, mais n’est pas installé pour le tester avec de longues données de colonne, est tkdiff .
utilisateur inconnu

Xxdiff affiche-t-il les lignes déplacées ? Ou montre-t-il simplement une ligne manquante dans un fichier et une ligne ajoutée dans l'autre? (J'ai essayé de construire xxdiff mais qmake a échoué et je constate qu'ils ne se donnent pas la peine de publier un paquet Debian).
Dan Dascalescu

@DanDascalescu: Aujourd'hui, je n'ai installé que tkdiff.
utilisateur inconnu

1

Sur la ligne de commande, je voudrais m'assurer d'ajouter de nouvelles lignes judicieuses avant de comparer des fichiers. Vous pouvez utiliser sed, awk, perl ou autre chose vraiment pour ajouter des sauts de ligne de manière systématique - assurez-vous de ne pas en ajouter trop.

Mais je trouve que le mieux est d’utiliser vim car il met en évidence les différences entre les mots. vim est bon s’il n’ya pas trop de différences et que les différences sont simples.


Bien que ce ne soit pas vraiment une réponse à la question, cette technique est plutôt efficace pour apprendre à comprendre les petites différences dans les lignes longues.
jknappen

1

kdiff3 est en train de devenir le visualiseur standard de diff graphiques sur Linux. C'est similaire à xxdiff , mais je pense que kdiff3 est meilleur. Cela fait beaucoup de choses, y compris votre demande d'afficher "les différences de caractère exactes entre deux lignes dans certains fichiers".


KDiff3 est extrêmement lent à mettre en évidence les différences en ligne dans les fichiers CSV. Je ne le recommanderais pas.
Dan Dascalescu Le

1

Si je lis bien votre question, je l’utilise diff -ypour ce genre de chose.

Il est beaucoup plus simple de comparer une comparaison côte à côte pour déterminer les lignes qui génèrent les différences.


1
Cela ne met pas en évidence la différence au sein de la ligne. Si vous avez une longue file d'attente, il est difficile de voir la différence. wdiff, git diff - mot-diff, vimgit, meld, kbdiff3, tkdiff font tout cela.
user2707671

1

J'ai eu le même problème et l'ai résolu avec PHP Fine Diff , un outil en ligne qui vous permet de spécifier la granularité. Je sais que ce n'est pas techniquement un outil * nix, mais je ne voulais pas vraiment télécharger un programme juste pour faire un diff unique, au niveau du personnage.


Certains utilisateurs ne peuvent pas télécharger de fichiers sensibles ou volumineux vers un outil en ligne aléatoire. Il existe de nombreux outils qui montrent les différences au niveau des lignes sans compromettre votre confidentialité.
Dan Dascalescu Le

Oui il y en a. Mais pour les diffs qui ne contiennent pas d'informations sensibles, les outils en ligne peuvent être une bonne solution.
pillravi

Les outils de différenciation en ligne ne prennent pas non plus en charge l’intégration en ligne de commande. Vous ne pouvez pas les utiliser à partir de votre flux de contrôle de version. Ils sont également beaucoup plus encombrants à utiliser (sélectionnez le fichier 1, sélectionnez le fichier 2, téléchargez) et ne peuvent pas fusionner.
Dan Dascalescu
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.