различие в линии


113

У меня есть несколько дампов SQL, которые я смотрю на различия между. diffочевидно, может показать мне разницу между двумя строками, но я схожу с ума, пытаясь найти, какие значения в длинном списке значений, разделенных запятыми, на самом деле являются теми, которые приводят к тому, что строки различаются.

Какой инструмент я могу использовать, чтобы указать точные символьные различия между двумя строками в определенных файлах?


Ответы:


93

Для этого есть wdiff , слово-diff.

На рабочем столе Meld может выделить различия в строке для вас.


8
Цветной wdiff:wdiff -w "$(tput bold;tput setaf 1)" -x "$(tput sgr0)" -y "$(tput bold;tput setaf 2)" -z "$(tput sgr0)" file1 file2
10

47
Для цвета установите colordiff , затем выполните:wdiff a b | colordiff
philfreo

Мелд на самом деле очень медленно (в минутах) показывает разницу между строками между файлами на основе строк.
Дан Даскалеску

Существует также dwdiffинструмент, который в основном совместим с, wdiffно также поддерживает цветной вывод и, возможно, некоторые другие функции. И это более доступно в некоторых дистрибутивах Linux, таких как Arch.
MarSoft

4
wdiff -n a b | colordiff, советует man colordiff.
Камиль Гудесюн

25

Еще один метод, использующий git-diff:

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

grep -v, если не интересует позиции различий.


2
Это именно то поведение, которое я пытался имитировать - я не понимал, что могу использовать git-diff без индексации одного из файлов.
раскрутка

1
--word-diff является ключевым параметром здесь. Спасибо!
user2707671

1
--no-index требуется, только если вы находитесь в рабочем каталоге git, а также foo и bar.
хн.

22

Я использовал vimdiffдля этого.

Вот скриншот (не мой), показывающий незначительные различия в одном или двух символах, которые очень хорошо выделяются. Краткий учебник тоже .


В моем случае не удалось обнаружить разницу, поэтому открывал файлы в gvim -d f1 f2, отдельные длинные строки были выделены как разные, однако фактическая разница была дополнительно выделена красным
zzapper

Я использовал vim навсегда, но понятия не имел о vimdiff!
Митч

И есть diffchar.vim для различий на уровне персонажей.

2
Как бы я ни любил vim и vimdiff, алгоритм vimdiff для выделения различий в строке довольно простой. Кажется, что он просто удаляет общий префикс и суффикс и выделяет все между ними как отличающиеся. Это работает, если все символы, которые изменились, сгруппированы вместе, но если они распределены, это не работает хорошо. Это также ужасно для завернутого в слова текста.
Лоуренс Гонсалвес

Для длинных строк, как в OP vimdiff -c 'set wrap' -c 'wincmd w' -c 'set wrap' a b, предлагается stackoverflow.com/a/45333535/2097284 .
Камиль Гудесюн

6

Вот метод "... волосы собаки, которая укусила тебя" ... подтолкнула
diffтебя к этой точке; использовать его, чтобы вы пошли дальше ...

Вот результат использования пар строк образца ... указывает на вкладку

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

Вот сценарий ... Вам просто нужно как-то найти пары линий ... (я использовал diff только один раз (дважды?) До сегодняшнего дня, так что я не знаю его много вариантов, и разбираюсь с вариантами для этого сценария мне хватило, на один день :) .. думаю, он должен быть достаточно простым, но я должен на кофе-брейк ....

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

wdiffна самом деле очень старый метод сравнения файлов слово за словом. Он работал, переформатируя файлы, затем используя их, diffчтобы найти различия и передавая их снова. Я сам предложил добавить контекст, так чтобы вместо пословного сравнения это делалось с каждым словом, окруженным другими словами «контекста». Это позволяет diff лучше синхронизировать себя на общих местах в файлах, особенно когда файлы в основном отличаются только несколькими блоками общих слов. Например, при сравнении текста на предмет плагиата или повторного использования.

dwdiffбыл позже создан из wdiff. Но dwdiff использует эту функцию переформатирования текста для хорошего эффекта в dwfilter. Это отличная разработка - это означает, что вы можете переформатировать один текст, чтобы он соответствовал другому, а затем сравнить их с помощью любого построчного графического средства отображения различий. Например, используя его с «диффузным» графическим diff ....

dwfilter file1 file2 diffuse -w

Это переформатирует file1в формат file2и дает это diffuseдля визуального сравнения. file2не изменяется, так что вы можете редактировать и объединять различия слов в него непосредственно в diffuse. Если вы хотите редактировать file1, вы можете добавить, -rчтобы отменить, какой файл переформатировать. Попробуйте, и вы найдете, что это очень мощный!

Я предпочитаю графический интерфейс (показанный выше), так diffuseкак он выглядит намного чище и полезнее. Также это отдельная программа на Python, что означает, что ее легко установить и распространить на другие системы UNIX.

Другие графические различия, кажется, имеют много зависимостей, но также могут быть использованы (на ваш выбор). К ним относятся kdiff3или xxdiff.


4

Используя в качестве основы решение @ Peter.O, я переписал его, чтобы внести ряд изменений.

введите описание изображения здесь

  • Он печатает каждую строку только один раз, используя цвет, чтобы показать вам различия.
  • Он не записывает никаких временных файлов, а вместо этого передает все.
  • Вы можете указать два имени файла, и он будет сравнивать соответствующие строки в каждом файле. ./hairOfTheDiff.sh file1.txt file2.txt
  • В противном случае, если вы используете исходный формат (один файл с каждой второй строкой, которую нужно сравнивать с предыдущим), вы можете просто передать его по конвейеру, для чтения файла не требуется. Взгляните на demoисточник; это может открыть дверь для причудливого трубопровода, чтобы не нуждаться в файлах для двух отдельных входов, используя pasteи несколько файловых дескрипторов.

Отсутствие выделения означает, что символ был в обеих строках, выделение означает, что он был в первой, а красный - во второй.

Цвета можно изменять с помощью переменных в верхней части скрипта, и вы даже можете полностью отказаться от цветов, используя обычные символы для выражения различий.

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

Вот простой однострочник:

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

Идея состоит в том, чтобы заменить запятые (или какой-либо разделитель, который вы хотите использовать) с использованием новой строки sed. diffзатем заботится обо всем остальном.


2
  • xxdiff: еще один инструмент - xxdiff (GUI), который должен быть установлен в первую очередь.
  • электронная таблица: для данных базы данных .csvлегко создать электронную таблицу, (A7==K7) ? "" : "diff"вставить формулу или аналогичное и вставить копию.

1
xxdiff выглядит как 80-е годы. Meld выглядит намного лучше, но он очень медленный для CSV-подобных файлов. Я обнаружил, что Diffuse - самый быстрый инструмент сравнения Linux.
Дан Даскалеску

@DanDascalescu: инструмент, который выполняет свою работу, всегда выглядит хорошо, независимо от того, сколько ему лет. Другой, который я использовал время от времени, но не установлен для тестирования его с длинными данными столбцов, это tkdiff .
пользователь неизвестен

Отображает ли xxdiff перемещенные строки ? Или просто показывает пропущенную строку в одном файле и добавленную в другом? (Я пытался собрать xxdiff, но qmake не удалось, и я вижу, что они не потрудились опубликовать пакет Debian).
Дан Даскалеску

@DanDascalescu: Сегодня у меня установлен только tkdiff.
пользователь неизвестен

1

В командной строке я хотел бы убедиться, что я добавил разумные новые строки, прежде чем сравнивать файлы. Вы можете использовать sed, awk, perl или что-то действительно, чтобы добавлять разрывы строк каким-то систематическим способом - убедитесь, что не добавляете слишком много.

Но я считаю, что лучше всего использовать vim, поскольку он выделяет различия в словах. vim хорош, если различий не так много, а различия просты.


Хотя этот метод не является действительно ответом на этот вопрос, он достаточно эффективен для изучения небольших различий в длинных очередях.
jknappen

1

kdiff3 становится стандартным средством просмотра различий GUI в Linux. Это похоже на xxdiff , но я думаю, что kdiff3 лучше. Он хорошо справляется со многими задачами, включая ваш запрос показать «точные различия символов между двумя строками в определенных файлах».


KDiff3 очень медленно выделяет встроенные различия в файлах CSV. Я бы не рекомендовал это.
Дан Даскалеску

1

Если я правильно читаю ваш вопрос, я использую diff -yдля такого рода вещи.

Это делает сравнение параллельным сравнением намного проще, чтобы найти, какие строки выбрасывают различия.


1
Это не подчеркивает разницу в строке. Если у вас длинная очередь, больно видеть разницу. wdiff, git diff --word-diff, vimgit, meld, kbdiff3, tkdiff - все это делает.
user2707671

1

У меня была та же проблема, и я решил ее с помощью PHP Fine Diff , онлайн-инструмента, который позволяет вам определять гранулярность. Я знаю, что это технически не инструмент * nix, но я действительно не хотел загружать программу просто для того, чтобы выполнить разовую разность уровней персонажа.


Некоторые пользователи не могут загружать конфиденциальные или большие файлы в случайный онлайн-инструмент. Существует множество инструментов, которые показывают различия на уровне линий без ущерба для вашей конфиденциальности.
Дан Даскалеску

Да это так. Но для различий, которые не содержат конфиденциальной информации, онлайн-инструменты могут быть хорошим решением.
Пилрави

Онлайн-инструменты сравнения также не поддерживают интеграцию с командной строкой. Вы не можете использовать их из потока управления версиями. Они также намного более громоздки в использовании (выберите файл 1, выберите файл 2, загрузите) и не могут выполнять слияние.
Дан Даскалеску
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.