行内の差分


113

違いを調べているSQLダンプがいくつかあります。diff明らかに2つの行の違いを示すことができますが、カンマ区切り値の長いリスト内のどの値が実際に行が異なるのかを見つけようとしています。

特定のファイルの2行間の正確な文字の違いを指摘するには、どのツールを使用できますか?


回答:


93

そのための単語差分であるwdiffがあります。

デスクトップでは、meldは行内の違いを強調表示できます。


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

47
色については、colordiffをインストールしてから、次を実行しますwdiff a b | colordiff
。– philfreo

実際、Meldは、行ベースのファイルと行内のファイルの違いを表示するのに非常に時間がかかります(数分)
ダンダスカレスク

また、dwdiff主に互換性wdiffがありますが、色付きの出力とおそらく他の機能もサポートするツールもあります。また、Archなどの一部のLinuxディストリビューションでより多く利用できます。
MarSoft

4
wdiff -n a b | colordiff、アドバイスしman colordiffます。
カミーユ・グドゥスヌ

25

git-diffを使用するもう1つの方法:

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の両方が同様である場合にのみ必要です。
xn。

22

私はこれに使用vimdiffしました。

ここに、 1つまたは2つの小さな文字の違いを示すスクリーンショット(私のスクリーンショットではありません)があります。あまりにも簡単なチュートリアル


私の場合はそう特に長い行が両方しかし、実際の差は、余分な赤で強調表示された異なるものとして強調されたの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 bstackoverflow.com/ a/ 45333535/2097284を提案します。
カミーユ・グドゥスヌ

6

ここに「あなたを噛んだ犬の髪」の方法
diffがあります... この点にあなたを連れて行きました; それを使用してさらに先に進みます...

サンプル行のペアを使用した場合の出力は次のとおりです... 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 
║  ║   ^ ║      ║     ║    ║          ║     ^

スクリプトは次のとおりです。行ペアを何らかの方法でフェレットアウトする必要があります。スクリプトは、私にとっては1日で十分でした:) ..それは十分にシンプルであるに違いないと思いますが、私はコーヒーブレイクを予定しています....

#
# 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ます。これはすばらしい開発です。つまり、あるテキストを別のテキストに合わせて再フォーマットし、行ごとのグラフィカルな差分表示を使用してそれらを比較できることを意味します。たとえば、「diffuse」グラフィカルdiffで使用します。

dwfilter file1 file2 diffuse -w

これは、再フォーマットfile1の形式にfile2し、それに与えるdiffuse視覚的な比較のために。file2は変更されていないため、で単語の違いを直接編集およびマージできますdiffuse。編集する場合file1-rどのファイルを再フォーマットするかを逆に追加できます。試してみると、非常に強力です。

グラフィカルなdiff(上記を参照)の好みは、diffuseはるかにクリーンで便利だと感じるためです。また、スタンドアロンのPythonプログラムであるため、他のUNIXシステムに簡単にインストールして配布できます。

他のグラフィカルな差分には多くの依存関係があるようですが、使用することもできます(選択)。これらには、kdiff3またはが含まれますxxdiff


4

@ Peter.Oのソリューションを基礎として使用して、多くの変更を加えるために書き直しました。

ここに画像の説明を入力してください

  • すべての行を一度だけ印刷し、色を使用して違いを示します。
  • 一時ファイルは作成せず、代わりにすべてをパイピングします。
  • 2つのファイル名を指定でき、各ファイルの対応する行を比較します。 ./hairOfTheDiff.sh file1.txt file2.txt
  • それ以外の場合、元の形式(2行ごとに前の行と比較する必要がある単一のファイル)を使用する場合は、単純にパイプするだけで、読み込むためにファイルが存在する必要はありません。demoソースを見てください。これによりpaste、複数のファイル記述子を使用して、2つの別々の入力用のファイルも必要ないように、空想的なパイピングへの扉が開かれる可能性があります。

ハイライトなしは、文字が両方の行にあったことを意味し、ハイライトは最初の行にあったことを意味し、赤は2番目の行にあったことを意味します。

色はスクリプトの上部にある変数を介して変更でき、通常の文字を使用して違いを表現することにより、色を完全に無視することさえできます。

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

アイデアは、カンマ(または使用する区切り文字)を使用して改行で置き換えることですseddiffその後、残りを処理します。


2
  • xxdiff:もう1つのツールはxxdiff(GUI)で、最初にインストールする必要があります。
  • スプレッドシート:データベースデータの場合、スプレッドシート.csvは簡単に作成でき、数式(A7==K7) ? "" : "diff"などを挿入してコピーアンドペーストできます。

1
xxdiffは80年代のように見えます。Meldはずっと良く見えますが、CSVのようなファイルの場合は非常に遅くなります。DiffuseはLinuxの最速のdiffツールであることがわかりました。
ダンダスカレスク

@DanDascalescu:どんなに古くても、仕事を終わらせるツールは常に問題なく見えます。もう1つは、時々使用しましたが、長い列データでテストするためにインストールされていないtkdiffです。
ユーザー不明

xxdiff は移動した行を表示しますか?または、1つのファイルに行があり、もう1つのファイルに追加された行が表示されるだけですか?(xxdiffをビルドしようとしましたが、qmakeが失敗し、Debianパッケージを公開することを気にしません)。
ダンダスカレスク

@DanDascalescu:今日、tkdiffのみがインストールされています。
ユーザー不明

1

コマンドラインで、ファイルを比較する前に適切な改行を追加するようにします。sed、awk、perlなどを実際に使用して、何らかの体系的な方法で改行を追加できます。ただし、追加しすぎないようにしてください。

しかし、単語の違いを強調するためにvimを使用するのが最善だと思います。vimは、あまり多くの違いがなく、違いが単純な場合に適しています。


実際には質問に対する答えではありませんが、この手法は長い行の小さな違いについて学ぶのにかなり効率的です。
-jknappen

1

kdiff3は、Linuxの標準GUI diffビューアになりつつあります。xxdiffと似ていますが、kdiff3の方が優れていると思います。「特定のファイルの2行間の正確な文字の違い」を表示する要求など、多くのことを行います。


KDiff3はCSVファイルのインラインの違いを強調するのが非常に遅いです。私はそれをお勧めしません。
ダンダスカレスク

1

私があなたの質問を正しく読んでいるなら、私diff -yはこの種のものに使用します。

どの行が違いを投げているのかを見つけるために、横並びの比較をはるかに簡単にします。


1
これは、行内の違いを強調しません。長い列がある場合、違いを見るのは苦痛です。wdiff、git diff --word-diff、vimgit、meld、kbdiff3、tkdiffはすべてこれを行います。
user2707671

1

同じ問題があり、粒度を指定できるオンラインツールPHP Fine Diffで解決しました。技術的には* nixツールではないことは知っていますが、一度だけ文字レベルの差分をとるためだけにプログラムをダウンロードしたくはありませんでした。


一部のユーザーは、重要なファイルや大きなファイルをランダムなオンラインツールにアップロードできません。プライバシーを損なうことなく、行レベルの違いを示すツールたくさんあります
ダンダスカレスク

はい、あります。ただし、機密情報を含まない差分の場合は、オンラインツールが適切なソリューションになります。
ピルラビ

オンラインdiffツールは、コマンドライン統合もサポートしていません。バージョン管理フローからそれらを使用することはできません。また、使用するのがはるかに面倒で(ファイル1を選択、ファイル2を選択、アップロード)、マージを実行できません。
ダンダスカレスク
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.