一行内的差异


113

我有一些SQL转储,我正在看它们之间的差异。diff显然可以向我展示两行之间的区别,但是我很努力地试图找出长逗号分隔值列表中的哪些值实际上是导致行不同的值。

我可以使用什么工具指出某些文件中两行之间的确切字符差异?


Answers:


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
2011年

47
对于颜色,请安装colordiff,然后执行以下操作:wdiff a b | colordiff
philfreo 2013年

在显示与基于行的文件之间的行内差异时,Meld实际上非常慢(几分钟)
Dan Dascalescu

还有一个dwdiff工具,大多数与兼容,wdiff但也支持彩色输出以及其他一些功能。而且它在某些Linux发行版(例如Arch)中更可用。
MarSoft's

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都必需时才是必需的。
xn。

22

我已经习惯vimdiff了。

这是一个截图(不是我的),显示了很小的一两个字符差异,非常明显。也是一个快速的教程


在我的情况下无法看出其中的区别,从而揭开了GVIM -d F1 F2特定排长队是这两个文件强调为不同但实际差异,在红色的额外突出
zzapper

我一直在使用vim,但是对vimdiff却一无所知!
mitchus

还有用于字符级差异的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。用它来带你进一步...

这是使用样本线对的输出... 表示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 
║  ║   ^ ║      ║     ║    ║          ║     ^

这是脚本。.您只需要以某种方式找出线对。.(在今天之前,我仅使用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在Windows中使用该文本重新格式化功能效果良好dwfilter。这是一个了不起的进步–这意味着您可以重新格式化一个文本以匹配另一个文本,然后使用任何逐行图形化diff显示器来比较它们。例如,将其与“ diffuse”图形差异一起使用。

dwfilter file1 file2 diffuse -w

重新格式化file1为的格式,file2diffuse进行可视化比较。file2未修改,因此您可以直接在中编辑和合并单词差异diffuse。如果要编辑file1,可以添加-r以重新格式化哪个文件。试试看,您会发现它非常强大!

我更喜欢图形差异(如上所示),diffuse因为它感觉更简洁,更有用。它还是一个独立的python程序,这意味着它很容易安装并分发到其他UNIX系统。

其他图形差异似乎有很多依赖性,但也可以使用(您选择)。这些包括kdiff3xxdiff


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')

想法是使用换行符替换逗号(或您希望使用的任何定界符)seddiff然后照顾其余的。


2
  • xxdiff:另一个工具是xxdiff(GUI),它必须首先安装。
  • 电子表格:对于数据库数据,.csv很容易制作一个电子表格,并(A7==K7) ? "" : "diff"插入公式或类似内容,然后复制粘贴。

1
xxdiff看起来像80年代。融合看起来好多了,但是对于类似CSV的文件来说却非常慢。我发现Diffuse是最快的Linux diff工具。
Dan Dascalescu

@DanDascalescu:不管看起来有多老,都能完成工作的工具看起来总是很好。我偶尔使用的另一个,是tkdiff,但没有安装它来对长列数据进行测试。
用户未知

xxdiff是否显示移动的行?还是只是在一个文件中显示缺失行,而在另一个文件中显示缺失行?(我尝试构建xxdiff,但qmake失败了,我发现他们不必费心发布Debian软件包)。
Dan Dascalescu

@DanDascalescu:今天,我只安装了tkdiff。
用户未知

1

在命令行上,我将确保在比较文件之前添加明智的换行符。您可以使用sed,awk,perl或其他任何东西来以某种系统的方式添加换行符-确保不要添加太多。

但是我发现最好是使用vim,因为它会突出显示单词的差异。如果差异不大且差异很简单,那么vim很好。


尽管这并不是对问题的真正答案,但该技术对于了解长行中的细微差异非常有效。
jknappen


1

如果我正确阅读了您的问题,那么我会使用diff -y这种方法。

它使并排比较的比较容易得多,以找出哪些行引发了差异。


1
这不会突出显示行中的差异。如果您的路线很长,那么看到差异就很痛苦。wdiff,git diff --word-diff,vimgit,meld,kbdiff3,tkdiff都可以做到这一点。
user2707671

1

我遇到了同样的问题,并使用PHP Fine Diff(一个允许您指定粒度的在线工具)解决了该问题。我知道从技术上讲它不是* nix工具,但我并不是真的想下载一个程序来进行一次字符级差异检查。


某些用户无法将敏感文件或大文件上传到随机的在线工具。有很多工具可以显示线级差异,而不会损害您的隐私。
Dan Dascalescu

是的,有。但是对于不包含敏感信息的差异,联机工具可能是一个很好的解决方案。
pillravi

在线差异工具也不支持命令行集成。您不能从版本控制流程中使用它们。它们使用起来也很麻烦(选择文件1,选择文件2,上传),并且无法合并。
Dan Dascalescu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.