diff all'interno di una linea


113

Ho alcune discariche sql che sto esaminando le differenze tra. diffpuò ovviamente mostrarmi la differenza tra due righe, ma mi sto facendo impazzire cercando di trovare quali valori nella lunga lista di valori separati da virgola sono in realtà quelli che fanno sì che le linee siano diverse.

Quale strumento posso usare per evidenziare le differenze esatte tra i caratteri tra due righe in determinati file?


Risposte:


93

C'è wdiff , la parola diff per questo.

Sul desktop, la combinazione può evidenziare le differenze all'interno di una linea per te.


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

47
Per il colore, installa colordiff , quindi wdiff a b | colordiff
esegui

La fusione in realtà è estremamente lenta (minuti) nel mostrare le differenze interne ai file basati su linea.
Dan Dascalescu,

C'è anche uno dwdiffstrumento che è principalmente compatibile con wdiffma supporta anche l'output colorato e probabilmente alcune altre funzionalità. Ed è più disponibile in alcune distribuzioni Linux come Arch.
MarSoft,

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

25

Solo un altro metodo che utilizza git-diff:

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

grep -v se non interessato alle posizioni dei diff.


2
Questo è esattamente il comportamento che stavo cercando di imitare - non avevo capito che avrei potuto usare git-diff senza che uno dei file fosse indicizzato.
spinup

1
--word-diff è l'opzione chiave qui. Grazie!
user2707671

1
--no-index è richiesto solo se ci si trova in una directory di lavoro git e lo sono anche foo e bar.
xn.

22

L'ho usato vimdiffper questo.

Ecco uno screenshot (non il mio) che mostra le differenze minori di uno o due personaggi che si distingue abbastanza bene. Un breve tutorial anche .


Nel mio caso non sono riuscito a individuare la differenza, quindi ho aperto i file in gvim -d f1 f2, le linee lunghe in particolare sono state entrambe evidenziate come diverse, tuttavia la differenza effettiva è stata ulteriormente evidenziata in rosso
zzapper

Uso vim da sempre, ma non avevo idea di vimdiff!
mitchus,

E c'è diffchar.vim per le differenze a livello di personaggio.

2
Per quanto io ami vim e vimdiff, l'algoritmo di vimdiff per evidenziare le differenze in una linea è piuttosto semplice. Sembra semplicemente eliminare il prefisso e il suffisso comuni ed evidenziare tutto tra come diversi. Funziona se tutti i personaggi che sono cambiati sono raggruppati insieme, ma se sono sparsi non funziona bene. È anche terribile per il testo racchiuso in parole.
Laurence Gonsalves il

Per le linee lunghe come nel PO vimdiff -c 'set wrap' -c 'wincmd w' -c 'set wrap' a b, suggerisce stackoverflow.com/a/45333535/2097284 .
Camille Goudeseune

6

Ecco un metodo "..hair of the dog that bit you" ...
diffti ha portato a questo punto; usalo per portarti oltre ...

Ecco l'output dell'utilizzo delle coppie di linee di esempio ... indica una 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 
║  ║   ^ ║      ║     ║    ║          ║     ^

Ecco lo script .. Devi solo scovare le coppie di linee in qualche modo .. (Ho usato diff solo una volta (due volte?) Prima di oggi, quindi non conosco le sue molte opzioni e selezionando le opzioni per questo la sceneggiatura mi è bastata, per un giorno :) .. Penso che debba essere abbastanza semplice, ma sono in pausa per una pausa caffè ....

#
# 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è in realtà un metodo molto vecchio per confrontare i file parola per parola. Ha funzionato riformattando i file, quindi usando diffper trovare le differenze e passandole di nuovo indietro. Io stesso ho suggerito di aggiungere un contesto, in modo che, anziché confrontarlo parola per parola, lo faccia con ogni parola circondata da altre parole "contestuali". Ciò consente al diff di sincronizzarsi molto meglio sui passaggi comuni nei file, specialmente quando i file sono per lo più diversi con solo pochi blocchi di parole comuni. Ad esempio, quando si confronta il testo per plagio o per il riutilizzo.

dwdiffè stato successivamente creato da wdiff. Ma dwdiff utilizza quella funzione di riformattazione del testo per ottenere buoni risultati dwfilter. Questo è un grande sviluppo: significa che è possibile riformattare un testo in modo che corrisponda a un altro e quindi confrontarlo utilizzando qualsiasi visualizzatore grafico diff riga per riga. Ad esempio, usandolo con diff "diffuso" grafico ....

dwfilter file1 file2 diffuse -w

Questo si riformatta file1nel formato di file2e lo dà diffuseper un confronto visivo. file2non è modificato, quindi puoi modificare e unire le differenze di parole direttamente in esso diffuse. Se si desidera modificare file1, è possibile aggiungere -rper invertire quale file viene riformattato. Provalo e scoprirai che è estremamente potente!

La mia preferenza per il diff grafico (mostrato sopra) è diffusecome sembra molto più pulito e più utile. Inoltre è un programma autonomo python, il che significa che è facile da installare e distribuire ad altri sistemi UNIX.

Altre differenze grafiche sembrano avere molte dipendenze, ma possono anche essere usate (a tua scelta). Questi includono kdiff3o xxdiff.


4

Utilizzando la soluzione di @ Peter.O come base, l'ho riscritta per apportare una serie di modifiche.

inserisci qui la descrizione dell'immagine

  • Stampa ogni riga una sola volta, usando il colore per mostrarti le differenze.
  • Non scrive alcun file temporaneo, eseguendo invece il piping di tutto.
  • Puoi fornire due nomi di file e confronterà le righe corrispondenti in ciascun file. ./hairOfTheDiff.sh file1.txt file2.txt
  • Altrimenti, se usi il formato originale (un singolo file con ogni seconda riga che deve essere confrontato con quello precedente) ora puoi semplicemente reindirizzarlo, non è necessario che esista un file da leggere. Dai un'occhiata alla demofonte; questo potrebbe aprire le porte a fantasiose tubazioni per non aver bisogno di file anche per due input separati, usando pastee più descrittori di file.

Nessun punto culminante indica che il personaggio si trovava in entrambe le righe, il momento saliente indica che era nel primo e il rosso indica che era nel secondo.

I colori sono modificabili attraverso le variabili nella parte superiore dello script e puoi persino rinunciare completamente ai colori usando caratteri normali per esprimere le differenze.

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

Ecco un semplice one-liner:

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

L'idea è di sostituire le virgole (o qualunque delimitatore che si desidera utilizzare) con le nuove righe usando sed. diffpoi si prende cura di tutto il resto.


2
  • xxdiff: un altro strumento è xxdiff (GUI), che deve essere installato per primo.
  • foglio di calcolo: per i dati del database, .csvè possibile creare facilmente un foglio di calcolo, inserire una formula (A7==K7) ? "" : "diff"o simile e copiarla.

1
xxdiff sembra gli anni '80. La combinazione sembra molto migliore ma è estremamente lenta per i file simili a CSV. Ho trovato Diffuse lo strumento diff Linux più veloce.
Dan Dascalescu,

@DanDascalescu: uno strumento che porta a termine il lavoro sembra sempre perfetto, non importa quanti anni abbia. Un altro, l'ho usato di tanto in tanto, ma non è installato per testarlo con dati di colonna lunghi, è tkdiff .
utente sconosciuto

Xxdiff mostra le linee spostate ? Oppure mostra solo una riga mancante in un file e una aggiunta nell'altro? (Ho provato a compilare xxdiff ma qmake non è riuscito e vedo che non si preoccupano di pubblicare un pacchetto Debian).
Dan Dascalescu,

@DanDascalescu: oggi ho solo tkdiff installato.
utente sconosciuto

1

Sulla riga di comando, vorrei assicurarmi di aggiungere nuove righe giudiziose prima di confrontare i file. Puoi usare sed, awk, perl o qualsiasi altra cosa per aggiungere interruzioni di linea in qualche modo sistematico - assicurati di non aggiungerne troppe però.

Ma trovo che il migliore sia usare vim in quanto mette in evidenza le differenze di parole. vim è buono se non ci sono troppe differenze e le differenze sono semplici.


Sebbene non sia davvero una risposta alla domanda, questa tecnica è piuttosto efficace per conoscere le piccole differenze nelle linee lunghe.
jknappen,

1

kdiff3 sta diventando il visualizzatore di differenze GUI standard su Linux. È simile a xxdiff , ma penso che kdiff3 sia migliore. Fa molte cose bene, inclusa la tua richiesta di mostrare "differenze di caratteri esatte tra due righe in determinati file".


KDiff3 è estremamente lento per evidenziare le differenze in linea nei file CSV. Non lo consiglierei.
Dan Dascalescu,

1

Se sto leggendo correttamente la tua domanda, lo uso diff -yper questo genere di cose.

Rende molto più semplice confrontare un confronto fianco a fianco per scoprire quali linee stanno gettando le differenze.


1
Ciò non evidenzia la differenza all'interno della linea. Se hai una lunga fila, è doloroso vedere la differenza. wdiff, git diff --word-diff, vimgit, meld, kbdiff3, tkdiff fanno tutti questo.
user2707671

1

Ho avuto lo stesso problema e l'ho risolto con PHP Fine Diff , uno strumento online che ti consente di specificare la granularità. So che tecnicamente non è uno strumento * nix, ma non volevo davvero scaricare un programma solo per fare una differenza a livello di personaggio una tantum.


Alcuni utenti non possono caricare file sensibili o di grandi dimensioni su uno strumento online casuale. Esistono molti strumenti che mostrano differenze a livello di linea senza compromettere la tua privacy.
Dan Dascalescu,

Si ci sono. Ma per i diff che non contengono informazioni sensibili, gli strumenti online possono essere una buona soluzione.
pillravi,

Anche gli strumenti diff online non supportano l'integrazione dalla riga di comando. Non puoi usarli dal flusso di controllo della versione. Sono anche molto più ingombranti da usare (selezionare il file 1, selezionare il file 2, caricare) e non possono fare l'unione.
Dan Dascalescu il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.