diff w linii


113

Mam kilka zrzutów SQL, na które patrzę różnice. diffmogę oczywiście pokazać różnicę między dwiema liniami, ale doprowadzam się do szału, próbując znaleźć, które wartości z długiej listy wartości oddzielonych przecinkami są tymi, które powodują różne linie.

Jakiego narzędzia mogę użyć, aby wskazać dokładne różnice znaków między dwiema liniami w niektórych plikach?


Odpowiedzi:


93

Jest wdiff , do tego słowo-diff.

Na pulpicie meld może dla Ciebie wyróżnić różnice w linii.


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

47
Dla koloru zainstaluj colordiff , a następnie:wdiff a b | colordiff
philfreo

Meld w rzeczywistości jest bardzo powolny (minuty) w pokazywaniu różnic między wierszami między plikami opartymi na linii.
Dan Dascalescu

Istnieje również dwdiffnarzędzie, które jest w większości kompatybilne, wdiffale obsługuje także kolorowe wydruki i prawdopodobnie niektóre inne funkcje. I jest bardziej dostępny w niektórych dystrybucjach Linuksa, takich jak Arch.
MarSoft,

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

25

Po prostu inna metoda wykorzystująca git-diff:

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

grep -v, jeśli nie jest zainteresowany pozycjami różnic.


2
Właśnie takie zachowanie próbowałem naśladować - nie zdawałem sobie sprawy, że mogę użyć git-diff bez indeksowania jednego z plików.
spinup 10.10.17

1
--word-diff jest tutaj kluczową opcją. Dzięki!
user2707671,

1
--no-index jest wymagany tylko, jeśli znajdujesz się w katalogu roboczym git i zarówno foo, jak i bar są również.
xn.

22

Użyłem vimdiffdo tego.

Oto zrzut ekranu (nie mój) pokazujący niewielką różnicę między jedną lub dwiema postaciami, która dość dobrze się wyróżnia. Szybki poradnik zbyt .


W moim przypadku nie dostrzegłem różnicy, więc otworzyłem pliki w gvim -d f1 f2, poszczególne długie linie zostały wyróżnione jako różne, ale rzeczywista różnica została dodatkowo podświetlona na czerwono
zzapper

Używam vima od zawsze, ale nie miałem pojęcia o vimdiff!
mitchus

I istnieje diffchar.vim dla diffów na poziomie postaci.

2
Mimo że uwielbiam vim i vimdiff, algorytm vimdiff do podkreślania różnic w linii jest dość prosty. Wydaje się, że po prostu usuwa wspólny prefiks i sufiks i wyróżnia wszystko jako różne. Działa to, jeśli wszystkie zmienione postacie są zgrupowane, ale jeśli są rozłożone, to nie działa dobrze. Jest to również okropne dla tekstu zawiniętego w słowa.
Laurence Gonsalves,

W przypadku długich linii, jak w PO vimdiff -c 'set wrap' -c 'wincmd w' -c 'set wrap' a b, sugeruje stackoverflow.com/a/45333535/2097284 .
Camille Goudeseune

6

Oto metoda „... włosa psa, który cię ugryzł” ...
diffdoprowadził cię do tego punktu; użyj go, aby przejść dalej ...

Oto wynik użycia par próbnych linii ... oznacza 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 
║  ║   ^ ║      ║     ║    ║          ║     ^

Oto skrypt .. Po prostu musisz jakoś wyłapać pary linii .. (Dzisiaj użyłem diff tylko raz (dwa razy?), Więc nie znam jego wielu opcji i sortuję opcje dla tego scenariusz był dla mnie wystarczający na jeden dzień :) .. Myślę, że musi być dość prosty, ale mam przerwę na kawę ....

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

wdiffjest tak naprawdę bardzo starą metodą porównywania plików słowo po słowie. Działało to poprzez diffponowne formatowanie plików, a następnie wykorzystywanie do znajdowania różnic i przekazywanie ich ponownie. Sam zasugerowałem dodanie kontekstu, aby zamiast porównywać słowo po słowie, robi to z każdym słowem otoczonym innymi słowami „kontekstowymi”. To pozwala diffowi zsynchronizować się znacznie częściej na wspólnych przejściach w plikach, szczególnie gdy pliki są w większości różne i mają tylko kilka bloków wspólnych słów. Na przykład podczas porównywania tekstu pod kątem plagiatu lub ponownego użycia.

dwdiffzostał później utworzony z wdiff. Ale dwdiff używa tej funkcji formatowania tekstu z dobrym skutkiem dwfilter. To świetny rozwój - oznacza, że ​​możesz sformatować jeden tekst, aby dopasować go do drugiego, a następnie porównać go za pomocą dowolnego graficznego wyświetlacza różnicowego linia po linii. Na przykład użycie go z „rozproszonym” graficznym diff ....

dwfilter file1 file2 diffuse -w

To zmienia file1format na format file2i daje to do diffusewizualnego porównania. file2jest niezmodyfikowany, więc możesz edytować i łączyć różnice między nimi bezpośrednio w diffuse. Jeśli chcesz edytować file1, możesz dodać -rdo tyłu, który plik jest sformatowany. Spróbuj, a przekonasz się, że jest niezwykle potężny!

Preferuję graficzną różnicę (pokazaną powyżej), diffuseponieważ wydaje się ona znacznie czystsza i bardziej użyteczna. Jest to także samodzielny program w języku Python, co oznacza, że ​​jest łatwy w instalacji i dystrybucji do innych systemów UNIX.

Inne różnice graficzne wydają się mieć wiele zależności, ale można ich również użyć (do wyboru). Należą do nich kdiff3lub xxdiff.


4

Korzystając z rozwiązania @ Peter.O jako podstawy, przepisałem go, aby wprowadzić szereg zmian.

wprowadź opis zdjęcia tutaj

  • Drukuje każdą linię tylko raz, używając koloru, aby pokazać różnice.
  • Nie zapisuje żadnych plików tymczasowych, zamiast tego wszystko potokuje.
  • Możesz podać dwie nazwy plików, aby porównać odpowiednie linie w każdym pliku. ./hairOfTheDiff.sh file1.txt file2.txt
  • W przeciwnym razie, jeśli użyjesz oryginalnego formatu (pojedynczy plik z co drugą linią wymagającą porównania z poprzednim), możesz teraz po prostu go przesłać, nie trzeba czytać żadnego pliku. Spójrz na demoźródło; może to otworzyć drzwi do fantazyjnych potoków, aby nie potrzebować plików dla dwóch osobnych danych wejściowych, przy użyciu pastewielu deskryptorów plików.

Brak wyróżnienia oznacza, że ​​znak był w obu liniach, wyróżnienie oznacza, że ​​było w pierwszym, a czerwone oznacza, że ​​było w drugim.

Kolory można zmieniać za pomocą zmiennych u góry skryptu, a nawet można całkowicie zrezygnować z kolorów, używając zwykłych znaków do wyrażania różnic.

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

Oto prosty jednowarstwowy:

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

Chodzi o to, aby zastąpić przecinki (lub dowolny separator, którego chcesz użyć) znakami nowej linii sed. diffnastępnie zajmuje się resztą.


2
  • xxdiff: Innym narzędziem jest xxdiff (GUI), które należy najpierw zainstalować.
  • arkusz kalkulacyjny: w przypadku danych bazy danych .csvłatwo jest utworzyć arkusz kalkulacyjny , a formułę (A7==K7) ? "" : "diff"lub podobny element wstawić i skopiować i wkleić.

1
xxdiff wygląda jak lata 80-te. Meld wygląda znacznie lepiej, ale jest bardzo wolny dla plików podobnych do CSV. Odkryłem, że Diffuse jest najszybszym narzędziem do różnicowania Linuksa.
Dan Dascalescu

@DanDascalescu: Narzędzie, które wykonuje zadanie, wygląda zawsze dobrze, bez względu na to, ile ma lat. Innym, z którego korzystałem od czasu do czasu, ale nie został zainstalowany w celu przetestowania go przy użyciu długich kolumnowych danych, jest tkdiff .
użytkownik nieznany

Czy xxdiff wyświetla przesunięte linie ? Czy może po prostu pokazuje brakujący wiersz w jednym pliku i dodany w drugim? (Próbowałem zbudować xxdiff, ale qmake nie powiodło się i widzę, że nie zawracają sobie głowy publikacją pakietu Debian).
Dan Dascalescu

@DanDascalescu: Dzisiaj mam tylko zainstalowany program tkdiff.
użytkownik nieznany

1

W wierszu poleceń upewnię się, że dodam rozsądne nowe wiersze przed porównaniem plików. Możesz użyć sed, awk, perl lub cokolwiek innego, aby dodać podział linii w jakiś systematyczny sposób - pamiętaj jednak, aby nie dodawać zbyt wielu.

Ale uważam, że najlepiej jest używać vim, ponieważ podkreśla różnice słów. vim jest dobry, jeśli nie ma zbyt wielu różnic, a różnice są proste.


Chociaż tak naprawdę nie jest to odpowiedź na pytanie, technika ta jest raczej skuteczna do nauki o małych różnicach w długich liniach.
jknappen

1

kdiff3 staje się standardową przeglądarką różnic GUI w systemie Linux. Jest podobny do xxdiff , ale myślę, że kdiff3 jest lepszy. Robi wiele rzeczy dobrze, w tym prośbę o pokazanie „dokładnych różnic znaków między dwiema liniami w niektórych plikach”.


KDiff3 jest bardzo powolny, aby podkreślić różnice między plikami CSV. Nie poleciłbym tego.
Dan Dascalescu

1

Jeśli poprawnie czytam twoje pytanie, używam diff -ydo tego rodzaju rzeczy.

To sprawia, że ​​porównywanie obok siebie jest znacznie łatwiejsze do znalezienia, które linie rzucają różnice.


1
Nie podkreśla to różnicy w linii. Jeśli masz długą linię, bolesna jest różnica. wdiff, git diff --word-diff, vimgit, meld, kbdiff3, tkdiff wszystko to robią.
user2707671,

1

Miałem ten sam problem i rozwiązałem go za pomocą PHP Fine Diff , narzędzia online, które pozwala określić szczegółowość. Wiem, że technicznie nie jest to narzędzie * nix, ale tak naprawdę nie chciałem pobierać programu tylko po to, aby zrobić jednorazową różnicę poziomów postaci.


Niektórzy użytkownicy nie mogą przesyłać poufnych lub dużych plików do losowego narzędzia online. Istnieje wiele narzędzi, które pokazują różnice na poziomie linii bez narażania prywatności użytkownika.
Dan Dascalescu

Tak, są. Ale w przypadku różnic, które nie zawierają poufnych informacji, narzędzia online mogą być dobrym rozwiązaniem.
pillravi

Narzędzia do porównywania online również nie obsługują integracji z wierszem poleceń. Nie można ich używać z poziomu kontroli wersji. Są również o wiele bardziej kłopotliwe w użyciu (wybierz plik 1, wybierz plik 2, prześlij) i nie można scalić.
Dan Dascalescu
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.