diff innerhalb einer Zeile


113

Ich habe einige SQL-Dumps, die ich auf die Unterschiede zwischen. diffkann mir natürlich den Unterschied zwischen zwei Zeilen zeigen, aber ich bin verrückt, wenn ich versuche herauszufinden, welche Werte in der langen Liste der durch Kommas getrennten Werte tatsächlich dazu führen, dass die Zeilen unterschiedlich sind.

Mit welchem ​​Tool kann ich auf die genauen Zeichenunterschiede zwischen zwei Zeilen in bestimmten Dateien hinweisen?


Antworten:


93

Es gibt wdiff , das Wort diff dafür.

Auf dem Desktop kann meld die Unterschiede innerhalb einer Zeile für Sie hervorheben.


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

47
Installieren Sie für die Farbe colordiff und führen Sie dann wdiff a b | colordiff
Folgendes aus


Es gibt auch dwdiffTools, die meistens kompatibel sind, wdiffaber auch farbige Ausgabe und wahrscheinlich einige andere Funktionen unterstützen. Und es ist in einigen Linux-Distributionen wie Arch besser verfügbar.
MarSoft

4
wdiff -n a b | colordiffberät man colordiff.
Camille Goudeseune

25

Nur eine andere Methode mit Git-Diff:

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

grep -v falls nicht interessiert an positionen der diffs.


2
Dies ist genau das Verhalten, das ich nachahmen wollte - ich wusste nicht, dass ich git-diff verwenden kann, ohne dass eine der Dateien indiziert wird.
Spinup

1
--word-diff ist hier die Schlüsseloption. Vielen Dank!
user2707671

1
--no-index ist nur erforderlich, wenn Sie sich in einem Git-Arbeitsverzeichnis befinden und sowohl foo als auch bar sind.
xn.

22

Ich habe dafür verwendet vimdiff.

Hier ist ein Screenshot (nicht von mir), der kleinere Unterschiede zwischen ein oder zwei Zeichen zeigt, die ziemlich gut herausragen. Ein kurzes Tutorial auch .


In meinem Fall konnte ich den Unterschied nicht erkennen, also wurden die Dateien in gvim -d f1 f2 geöffnet. Die einzelnen langen Zeilen wurden beide als unterschiedlich hervorgehoben. Der tatsächliche Unterschied wurde jedoch zusätzlich in Rot hervorgehoben
zzapper

Ich benutze vim schon immer, hatte aber keine Ahnung von vimdiff!
Mitchus

Und es gibt diffchar.vim für Unterschiede auf Zeichenebene.

2
So sehr ich vim und vimdiff liebe, so einfach ist der Algorithmus von vimdiff zum Hervorheben von Unterschieden in einer Zeile. Es scheint nur das gemeinsame Präfix und Suffix zu streifen und alles dazwischen als unterschiedlich hervorzuheben. Dies funktioniert, wenn alle Zeichen, die geändert wurden, in Gruppen zusammengefasst sind. Wenn sie jedoch verteilt sind, funktioniert dies nicht richtig. Es ist auch schrecklich für wortumbrochenen Text.
Laurence Gonsalves

Bei langen Leitungen wie im OP, vimdiff -c 'set wrap' -c 'wincmd w' -c 'set wrap' a bschlägt stackoverflow.com/a/45333535/2097284 .
Camille Goudeseune

6

Hier ist eine "..haar des Hundes, der dich beißt" -Methode ... brachte
diffdich an diesen Punkt; benutze es, um dich weiter zu bringen ...

Hier ist die Ausgabe aus der Verwendung der Sample-Linienpaare ... zeigt eine TAB an

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

Hier ist das Skript. Sie müssen nur die Leitungspaare irgendwie heraussuchen. (Ich habe Diff bisher nur einmal (zweimal?) Verwendet, daher kenne ich die vielen Optionen nicht und sortiere die Optionen dafür aus Drehbuch war genug für mich, für einen Tag :) .. Ich denke, es muss einfach genug sein, aber ich muss eine Kaffeepause einlegen ....

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

wdiffist eigentlich eine sehr alte Methode, um Dateien Wort für Wort zu vergleichen. Es funktionierte, indem Dateien neu formatiert, dann mit Hilfe diffvon Unterschieden gesucht und wieder zurückgegeben wurden. Ich selbst habe vorgeschlagen, den Kontext hinzuzufügen, damit er nicht wortweise verglichen wird, sondern mit jedem Wort, das von anderen "Kontext" -Wörtern umgeben ist. Auf diese Weise kann sich der Diff bei häufigen Passagen in Dateien viel besser synchronisieren, insbesondere wenn sich Dateien mit nur wenigen Blöcken gemeinsamer Wörter zumeist unterscheiden. Zum Beispiel beim Vergleichen von Text auf Plagiate oder bei der Wiederverwendung.

dwdiffwurde später erstellt von wdiff. Aber dwdiff nutzt diese Funktion zur Neuformatierung von Text, um eine gute Wirkung zu erzielen dwfilter. Dies ist eine großartige Entwicklung - es bedeutet, dass Sie einen Text neu formatieren können, um ihn mit einem anderen abzugleichen, und diese dann mit jedem zeilenweisen grafischen Diff-Display vergleichen können. Zum Beispiel mit "diffusem" grafischem Diff ....

dwfilter file1 file2 diffuse -w

Dies formatiert file1das Format neu file2und gibt es diffusefür einen visuellen Vergleich an. file2ist unverändert, so dass Sie Wortunterschiede direkt in bearbeiten und zusammenführen können diffuse. Wenn Sie bearbeiten möchten, file1können Sie hinzufügen, -rum umzukehren, welche Datei neu formatiert wird. Probieren Sie es aus und Sie werden feststellen, dass es extrem leistungsstark ist!

Ich bevorzuge den grafischen Unterschied (siehe oben), diffuseda er sich viel sauberer und nützlicher anfühlt. Es ist auch ein eigenständiges Python-Programm, was bedeutet, dass es einfach zu installieren und auf andere UNIX-Systeme zu verteilen ist.

Andere grafische Unterschiede scheinen viele Abhängigkeiten zu haben, können aber auch verwendet werden (Sie haben die Wahl). Dazu gehören kdiff3oder xxdiff.


4

Auf der Grundlage der @ Peter.O- Lösung habe ich sie umgeschrieben, um einige Änderungen vorzunehmen.

Bildbeschreibung hier eingeben

  • Jede Zeile wird nur einmal gedruckt, wobei die Unterschiede farblich hervorgehoben werden.
  • Es werden keine temporären Dateien geschrieben, stattdessen wird alles weitergeleitet.
  • Sie können zwei Dateinamen angeben, und es werden die entsprechenden Zeilen in jeder Datei verglichen. ./hairOfTheDiff.sh file1.txt file2.txt
  • Wenn Sie das Originalformat verwenden (eine einzelne Datei, bei der jede zweite Zeile mit der vorherigen verglichen werden muss), müssen Sie sie jetzt einfach einlesen. Es muss keine Datei zum Lesen vorhanden sein. Schauen Sie sich demodie Quelle an. Dies kann die Tür für ausgefallene Rohrleitungen öffnen, damit keine Dateien für zwei separate Eingaben mit pasteund mehreren Dateideskriptoren benötigt werden.

Keine Hervorhebung bedeutet, dass sich das Zeichen in beiden Zeilen befand. Hervorhebung bedeutet, dass es sich in der ersten befand, und Rot bedeutet, dass es sich in der zweiten befand.

Die Farben können über Variablen oben im Skript geändert werden. Sie können sogar ganz auf Farben verzichten, indem Sie normale Zeichen verwenden, um Unterschiede auszudrücken.

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

Hier ist ein einfacher Einzeiler:

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

Die Idee ist, Kommas (oder welche Trennzeichen Sie verwenden möchten) durch Zeilenumbrüche zu ersetzen sed. diffkümmert sich dann um den Rest.


2
  • xxdiff: Ein weiteres Tool ist xxdiff (GUI), das zuerst installiert werden muss.
  • Tabellenkalkulation: Für Datenbankdaten kann auf .csveinfache Weise eine Tabellenkalkulation erstellt und eine Formel (A7==K7) ? "" : "diff"oder Ähnliches eingefügt und kopiert und eingefügt werden.

1
xxdiff sieht aus wie in den 80ern. Meld sieht viel besser aus, ist aber für CSV-ähnliche Dateien extrem langsam. Ich habe festgestellt, dass Diffuse das schnellste Linux-Diff-Tool ist.
Dan Dascalescu

@DanDascalescu: Ein Tool, das die Arbeit erledigt, sieht immer gut aus, egal wie alt es aussieht. Eine andere, die ich gelegentlich verwendete, aber nicht zum Testen mit langen Spaltendaten installiert habe, ist tkdiff .
Benutzer unbekannt

Zeigt xxdiff verschobene Linien an ? Oder zeigt es nur eine fehlende Zeile in einer Datei und eine hinzugefügte in der anderen? (Ich habe versucht, xxdiff zu erstellen, aber qmake ist fehlgeschlagen und ich sehe, dass sie sich nicht die Mühe machen, ein Debian-Paket zu veröffentlichen).
Dan Dascalescu

@DanDascalescu: Heute habe ich nur tkdiff installiert.
Benutzer unbekannt

1

In der Befehlszeile würde ich vor dem Vergleichen von Dateien sicherstellen, dass vernünftige neue Zeilen hinzugefügt werden. Sie können sed, awk, perl oder alles andere verwenden, um Zeilenumbrüche auf systematische Weise einzufügen. Achten Sie jedoch darauf, nicht zu viele Zeilen hinzuzufügen.

Aber ich finde, das Beste ist, vim zu verwenden, da es die Wortunterschiede hervorhebt. vim ist gut, wenn es nicht zu viele unterschiede gibt und die unterschiede einfach sind.


Obwohl diese Technik keine wirkliche Antwort auf die Frage ist, ist sie sehr effizient, um kleine Unterschiede in langen Schlangen zu erkennen.
Jknappen

1

kdiff3 wird zum Standard-GUI-Diff-Viewer unter Linux. Es ist ähnlich wie xxdiff , aber ich denke, kdiff3 ist besser. Es macht viele Dinge gut, einschließlich Ihrer Aufforderung, "genaue Zeichenunterschiede zwischen zwei Zeilen in bestimmten Dateien" anzuzeigen.


KDiff3 ist extrem langsam , um die Inline-Unterschiede in CSV-Dateien hervorzuheben. Ich würde es nicht empfehlen.
Dan Dascalescu

1

Wenn ich Ihre Frage richtig lese, verwende ich diff -yfür solche Dinge.

Das Vergleichen eines Nebeneinander-Vergleichs vereinfacht das Auffinden, welche Linien die Unterschiede auslösen.


1
Dies hebt den Unterschied innerhalb der Zeile nicht hervor. Wenn Sie eine lange Schlange haben, ist es mühsam, den Unterschied zu erkennen. wdiff, git diff --word-diff, vimgit, meld, kbdiff3, tkdiff machen das alles.
user2707671

1

Ich hatte das gleiche Problem und löste es mit PHP Fine Diff , einem Online-Tool, mit dem Sie die Granularität festlegen können. Ich weiß, dass es technisch gesehen kein * nix-Tool ist, aber ich wollte eigentlich kein Programm herunterladen, nur um einen einmaligen Unterschied auf Zeichenebene zu machen.


Einige Benutzer können keine vertraulichen oder großen Dateien in ein zufälliges Online-Tool hochladen. Es gibt eine Vielzahl von Tools, die Unterschiede in der Zeilenebene anzeigen, ohne Ihre Privatsphäre zu beeinträchtigen.
Dan Dascalescu

Ja, das gibt es. Für Diffs, die keine vertraulichen Informationen enthalten, können Online-Tools eine gute Lösung sein.
Pillravi

Online-Diff-Tools unterstützen auch keine Befehlszeilenintegration. Sie können sie nicht aus Ihrem Versionskontrollfluss verwenden. Sie sind auch weitaus umständlicher zu verwenden (Datei 1 auswählen, Datei 2 auswählen, hochladen) und können nicht zusammengeführt werden.
Dan Dascalescu
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.