diff ภายในหนึ่งบรรทัด


113

ฉันมี sql dumps ที่ฉันกำลังดูความแตกต่างระหว่าง 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
l0b0

47
สำหรับสีให้ติดตั้งcolordiffจากนั้นทำ:wdiff a b | colordiff
philfreo

จริง ๆ แล้ว Meld นั้นช้ามาก (นาที)เมื่อแสดงความแตกต่างของอินทราระหว่างบรรทัดกับไฟล์
Dan Dascalescu

นอกจากนี้ยังมีdwdiffเครื่องมือที่เข้ากันได้เป็นส่วนใหญ่wdiffแต่ยังรองรับการแสดงผลสี และมันมีอยู่ในลีนุกซ์บางรุ่นเช่น Arch
MarSoft

4
wdiff -n a b | colordiffman colordiffให้คำแนะนำแก่
Camille Goudeseune

25

อีกวิธีหนึ่งที่ใช้ git-diff:

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

grep -v หากไม่สนใจตำแหน่งของ diffs


2
นี่เป็นพฤติกรรมที่ฉันพยายามเลียนแบบ - ไม่ทราบว่าฉันสามารถใช้ git-diff ได้โดยไม่ต้องมีไฟล์ใดไฟล์หนึ่งที่ถูกทำดัชนี
spinup

1
- word-diff เป็นตัวเลือกที่สำคัญที่นี่ ขอบคุณ!
user2707671

1
- ไม่มีดัชนีจะต้องเฉพาะถ้าคุณอยู่ในไดเรกทอรีการทำงานคอมไพล์และทั้งฟูและบาร์ก็เช่นกัน
xn

22

ฉันเคยใช้vimdiffสิ่งนี้

ต่อไปนี้เป็นภาพหน้าจอ (ไม่ใช่ของฉัน) แสดงความแตกต่างเล็กน้อยของอักขระหนึ่งหรือสองตัวที่โดดเด่น รวดเร็วกวดวิชาเกินไป


ในกรณีของฉันไม่สามารถมองเห็นความแตกต่างได้ดังนั้นให้เปิดไฟล์ใน gvim -d f1 f2 เส้นที่ยาวเป็นพิเศษถูกเน้นว่าแตกต่างกัน แต่ความแตกต่างที่แท้จริงถูกเน้นเป็นสีแดงเป็นพิเศษ
zzapper

ฉันใช้เสียงเรียกเข้าตลอด แต่ก็ไม่มีความคิดเกี่ยวกับ vimdiff!
mitchus

และมีdiffchar.vimสำหรับ diffs ระดับตัวละคร

2
เท่าที่ฉันชอบ vim และ vimdiff อัลกอริทึมของ vimdiff สำหรับการเน้นความแตกต่างในบรรทัดนั้นเป็นพื้นฐานที่ค่อนข้างดี ดูเหมือนว่าจะตัดคำนำหน้าและคำต่อท้ายออกและเน้นทุกสิ่งที่แตกต่างกัน วิธีนี้ใช้ได้ผลถ้าตัวละครทั้งหมดที่เปลี่ยนถูกจัดกลุ่มเข้าด้วยกัน แต่ถ้ามันถูกกระจายออกไปมันจะทำงานได้ไม่ดี นอกจากนี้ยังน่ากลัวสำหรับข้อความที่ห่อคำ
Laurence Gonsalves

สำหรับสายยาวเช่นเดียวกับใน OP ที่vimdiff -c 'set wrap' -c 'wincmd w' -c 'set wrap' a bแสดงให้เห็นstackoverflow.com/a/45333535/2097284
Camille Goudeseune

6

นี่คือวิธี "..hair ของสุนัขที่กัดคุณ" ...
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 สามารถซิงโครไนซ์ตัวเองในข้อความธรรมดาในไฟล์ได้ดีขึ้นโดยเฉพาะเมื่อไฟล์ส่วนใหญ่แตกต่างกันด้วยคำทั่วไปเพียงไม่กี่บล็อก ตัวอย่างเช่นเมื่อเปรียบเทียบข้อความสำหรับลอกเลียนแบบหรือนำกลับมาใช้ใหม่

dwdiffwdiffถูกสร้างขึ้นในภายหลังจาก แต่ จะใช้ฟังก์ชั่นการจัดรูปแบบข้อความเพื่อผลดีในdwdiff dwfilterนี่คือการพัฒนาที่ยอดเยี่ยม - หมายความว่าคุณสามารถจัดรูปแบบข้อความใหม่ให้ตรงกับข้อความอื่นแล้วเปรียบเทียบกับข้อความเหล่านั้นโดยใช้ตัวแสดงผล diff แบบกราฟิกต่อบรรทัด ตัวอย่างเช่นใช้กับ "diffuse" กราฟิก diff ....

dwfilter file1 file2 diffuse -w

ฟอร์แมตใหม่นี้file1เป็นรูปแบบfile2และมอบdiffuseการเปรียบเทียบแบบภาพให้เห็น คือยังไม่แปรเพื่อให้คุณสามารถแก้ไขและผสานความแตกต่างของคำว่าเป็นมันโดยตรงในfile2 diffuseหากคุณต้องการแก้ไขfile1คุณสามารถเพิ่ม-rเพื่อย้อนกลับไฟล์ที่ถูกฟอร์แมตใหม่ได้ ลองแล้วคุณจะพบว่ามันมีพลังมาก!

ความพึงพอใจของฉันสำหรับดิฟแบบกราฟิก (ดังที่แสดงด้านบน) นั้นdiffuseเป็นเพราะมันรู้สึกว่าสะอาดกว่าและมีประโยชน์มากกว่า นอกจากนี้ยังเป็นโปรแกรม python แบบสแตนด์อโลนซึ่งหมายความว่าง่ายต่อการติดตั้งและแจกจ่ายไปยังระบบ UNIX อื่น ๆ

ส่วนต่างกราฟิกอื่น ๆ ดูเหมือนจะมีการพึ่งพามากมาย แต่ยังสามารถใช้งานได้ (คุณเลือกได้) เหล่านี้รวมถึงหรือ kdiff3xxdiff


4

การใช้โซลูชันของ @ Peter.O เป็นพื้นฐานฉันเขียนใหม่เพื่อทำการเปลี่ยนแปลงจำนวนมาก

ป้อนคำอธิบายรูปภาพที่นี่

  • มันพิมพ์ทุกบรรทัดเพียงครั้งเดียวโดยใช้สีเพื่อแสดงความแตกต่าง
  • มันไม่ได้เขียนไฟล์ temp ใด ๆ และวางท่อทุกอย่างแทน
  • คุณสามารถให้ชื่อไฟล์สองชื่อและมันจะเปรียบเทียบบรรทัดที่เกี่ยวข้องในแต่ละไฟล์ ./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เป็นเครื่องมือ diff Linux ที่เร็วที่สุด
Dan Dascalescu

@DanDascalescu: เครื่องมือที่ทำให้งานดูดีอยู่เสมอไม่ว่าจะดูเก่าแค่ไหน อีกคนหนึ่งที่ผมใช้เป็นครั้งคราว แต่ไม่ได้ติดตั้งเพื่อทดสอบมีความยาวข้อมูลคอลัมน์เป็นtkdiff
ผู้ใช้ที่ไม่รู้จัก

xxdiff แสดงบรรทัดที่ย้ายหรือไม่ หรือมันเพิ่งแสดงบรรทัดที่ขาดหายไปในไฟล์เดียวและอีกหนึ่งไฟล์ที่เพิ่มเข้ามาใน? (ฉันพยายามสร้าง xxdiff แต่ qmake ล้มเหลวและฉันเห็นว่าพวกเขาไม่สนใจที่จะเผยแพร่แพ็คเกจ Debian)
Dan Dascalescu

@DanDascalescu: วันนี้ฉันเพิ่งติดตั้ง tkdiff เท่านั้น
ผู้ใช้ที่ไม่รู้จัก

1

ในบรรทัดคำสั่งฉันจะทำให้แน่ใจว่าฉันเพิ่มบรรทัดใหม่ที่รอบคอบก่อนที่จะเปรียบเทียบไฟล์ คุณสามารถใช้ sed, awk, perl หรืออะไรก็ได้เพื่อเพิ่มตัวแบ่งบรรทัดในวิธีที่เป็นระบบ - อย่าลืมเพิ่มมากเกินไป

แต่ฉันคิดว่าสิ่งที่ดีที่สุดคือการใช้เสียงเรียกเข้าเนื่องจากมันเน้นความแตกต่างของคำ เป็นกลุ่มที่ดีถ้าไม่มีความแตกต่างมากเกินไปและความแตกต่างนั้นง่าย


แม้ว่าจะไม่ได้คำตอบสำหรับคำถามเทคนิคนี้ค่อนข้างมีประสิทธิภาพในการเรียนรู้เกี่ยวกับความแตกต่างเล็ก ๆ ในสายยาว
jknappen

1

kdiff3กำลังกลายเป็นโปรแกรมดูมาตรฐาน GUI diff บน Linux มันคล้ายกับxxdiffแต่ฉันคิดว่า kdiff3 ดีกว่า มันทำได้หลายอย่างด้วยกันรวมถึงคำขอของคุณที่จะแสดง "ความแตกต่างของตัวอักษรที่แน่นอนระหว่างสองบรรทัดในไฟล์บางไฟล์"


KDiff3 ช้ามากเพื่อเน้นความแตกต่างแบบอินไลน์ในไฟล์ CSV ฉันจะไม่แนะนำที่นี่
Dan Dascalescu

1

ถ้าฉันอ่านคำถามของคุณถูกต้องฉันจะใช้diff -yสิ่งนี้

มันทำให้การเปรียบเทียบแบบเคียงข้างกันง่ายกว่ามากในการค้นหาว่าบรรทัดใดที่ทำให้เกิดความแตกต่าง


1
สิ่งนี้ไม่เน้นความแตกต่างภายในบรรทัด หากคุณมีเส้นยาวมันจะเจ็บปวดมากเมื่อเห็นความแตกต่าง wdiff, git diff --word-diff, vimgit, meld, kbdiff3, tkdiff ทั้งหมดทำสิ่งนี้
user2707671

1

ฉันมีปัญหาเดียวกันและแก้ไขด้วยPHP Fine Diffซึ่งเป็นเครื่องมือออนไลน์ที่ช่วยให้คุณระบุรายละเอียดได้ ฉันรู้ว่ามันไม่ใช่เครื่องมือ * ในทางเทคนิค แต่ฉันไม่ต้องการดาวน์โหลดโปรแกรมเพียงเพื่อให้ได้ระดับตัวอักษรที่ต่างกัน


ผู้ใช้บางคนไม่สามารถอัปโหลดไฟล์สำคัญหรือไฟล์ขนาดใหญ่ไปยังเครื่องมือออนไลน์แบบสุ่ม มีเครื่องมือมากมายที่แสดงความแตกต่างของระดับบรรทัดโดยไม่กระทบต่อความเป็นส่วนตัวของคุณ
Dan Dascalescu

ใช่แล้วล่ะ. แต่สำหรับส่วนต่างที่ไม่มีข้อมูลที่ละเอียดอ่อนเครื่องมือออนไลน์อาจเป็นทางออกที่ดี
pillravi

เครื่องมือดิฟแบบออนไลน์ยังไม่สนับสนุนการรวมบรรทัดคำสั่ง คุณไม่สามารถใช้งานได้จากโฟลว์การควบคุมเวอร์ชันของคุณ พวกเขายังยุ่งยากกว่าการใช้งาน (เลือกไฟล์ 1 เลือกไฟล์ 2 อัปโหลด) และไม่สามารถผสานได้
Dan Dascalescu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.