diff dentro de uma linha


113

Eu tenho alguns despejos sql que eu estou olhando para as diferenças entre. diffobviamente, pode me mostrar a diferença entre duas linhas, mas estou me deixando louco tentando descobrir quais valores na longa lista de valores separados por vírgula são realmente os que fazem com que as linhas sejam diferentes.

Que ferramenta posso usar para apontar as diferenças exatas de caracteres entre duas linhas em determinados arquivos?


Respostas:


93

wdiff , a palavra diff para isso.

Na área de trabalho, o meld pode destacar as diferenças dentro de uma linha para você.


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

47
Para a cor, instale colordiff , em seguida, fazer:wdiff a b | colordiff
philfreo

Na verdade, o Meld é extremamente lento (minutos) para mostrar as diferenças entre linhas entre os arquivos baseados em linha.
Dan Dascalescu

Também existe uma dwdiffferramenta que é compatível principalmente, wdiffmas também suporta saída colorida e provavelmente alguns outros recursos. E está mais disponível em algumas distribuições Linux como o Arch.
MarSoft 31/08/19

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

25

Apenas outro método usando git-diff:

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

grep -v se não estiver interessado nas posições dos diffs.


2
Este é exatamente o comportamento que eu estava tentando imitar - não sabia que poderia usar o git-diff sem que um dos arquivos fosse indexado.
spinup 10/10

11
--word-diff é a opção principal aqui. Obrigado!
user2707671

11
--no-index é necessário apenas se você estiver em um diretório de trabalho git e foo e bar também.
xn.

22

Eu usei vimdiffpara isso.

Aqui está uma captura de tela (não minha) mostrando pequenas diferenças de um ou dois caracteres que se destacam muito bem. Um tutorial rápido também .


No meu caso não poderia detectar a diferença assim que abriu os arquivos em gvim -d F1 F2 as longas filas particulares eram tanto destaque como sendo diferente no entanto a diferença real foi extra destacadas em vermelho
ZZAPPER

Eu uso o vim desde sempre, mas não fazia ideia do vimdiff!
Mitchus 03/04

E existe diffchar.vim para diferenças no nível do personagem.

2
Por mais que eu ame vim e vimdiff, o algoritmo de vimdiff para destacar diferenças em uma linha é bastante básico. Parece apenas retirar o prefixo e o sufixo comuns e destacar tudo entre os mesmos como diferente. Isso funciona se todos os personagens que foram alterados estiverem agrupados, mas se estiverem espalhados, não funcionará bem. Também é terrível para texto com quebra de linha.
Laurence Gonsalves

Para linhas longas como no OP vimdiff -c 'set wrap' -c 'wincmd w' -c 'set wrap' a b, sugere stackoverflow.com/a/45333535/2097284 .
Camille Goudeseune

6

Aqui está um método "... cabelo do cachorro que mordeu você" ...
diffchegou a esse ponto; use-o para levar você mais longe ...

Aqui está a saída do uso dos pares de linhas de amostra ... indica um 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 
║  ║   ^ ║      ║     ║    ║          ║     ^

Aqui está o script. Você só precisa descobrir os pares de linhas de alguma forma. (Eu usei o diff apenas uma vez (duas vezes?) Antes de hoje, então não conheço suas muitas opções e resolvo as opções para isso. script foi o suficiente para mim, por um dia :) .. Eu acho que deve ser simples o suficiente, mas devo fazer uma pausa para o café ....

#
# 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é realmente um método muito antigo de comparar arquivos palavra por palavra. Ele funcionou reformatando arquivos e depois usando diffpara encontrar diferenças e devolvê-lo novamente. Eu próprio sugeri adicionar contexto, para que, em vez de comparar palavra por palavra, o faça com cada palavra cercada por outras palavras de "contexto". Isso permite que o diff se sincronize em passagens comuns em arquivos muito melhor, especialmente quando os arquivos são principalmente diferentes com apenas alguns blocos de palavras comuns. Por exemplo, ao comparar texto para plágio ou reutilização.

dwdifffoi criado mais tarde wdiff. Mas dwdiff usa essa função de reformatação de texto com bons resultados em dwfilter. Esse é um ótimo desenvolvimento - significa que você pode reformatar um texto para coincidir com outro e compará-lo usando qualquer visualizador gráfico de linha por linha. Por exemplo, usando-o com diferenças gráficas "difusas" ....

dwfilter file1 file2 diffuse -w

Isso reformata file1o formato de file2e fornece isso diffusepara uma comparação visual. file2não é modificado, para que você possa editar e mesclar diferenças de palavras diretamente nele diffuse. Se você deseja editar file1, você pode adicionar -rpara reverter qual arquivo é reformatado. Experimente e você verá que é extremamente poderoso!

Minha preferência pelo diff gráfico (mostrado acima) é diffusecomo parece muito mais limpo e mais útil. Além disso, é um programa python independente, o que significa que é fácil de instalar e distribuir para outros sistemas UNIX.

Outras diferenças gráficas parecem ter muitas dependências, mas também podem ser usadas (sua escolha). Estes incluem kdiff3ou xxdiff.


4

Usando a solução do @ Peter.O como base, eu a reescrevi para fazer várias alterações.

insira a descrição da imagem aqui

  • Só imprime todas as linhas uma vez, usando cores para mostrar as diferenças.
  • Ele não grava nenhum arquivo temporário, canalizando tudo.
  • Você pode fornecer dois nomes de arquivos e ele comparará as linhas correspondentes em cada arquivo. ./hairOfTheDiff.sh file1.txt file2.txt
  • Caso contrário, se você usar o formato original (um único arquivo com cada segunda linha precisando ser comparado ao anterior) agora poderá simplesmente inseri-lo, nenhum arquivo precisa existir para ser lido. Dê uma olhada na demofonte; isso pode abrir a porta para tubulações sofisticadas, a fim de não precisar de arquivos para duas entradas separadas também, usando pastee vários descritores de arquivos.

Sem destaque significa que o personagem estava nas duas linhas, destaque significa que estava na primeira e vermelho significa que estava na segunda.

As cores são alteráveis ​​por meio de variáveis ​​na parte superior do script e você pode até renunciar inteiramente às cores usando caracteres normais para expressar diferenças.

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

Aqui está uma simples linha:

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

A idéia é substituir vírgulas (ou qualquer delimitador que você deseja usar) por novas linhas usando sed. diffdepois cuida do resto.


2
  • xxdiff: Outra ferramenta é o xxdiff (GUI), que deve ser instalado primeiro.
  • planilha: para dados do banco de dados, .csvé fácil criar uma planilha a partir de , e uma fórmula (A7==K7) ? "" : "diff"ou similar é inserida e copiada.

11
xxdiff parece com os anos 80. O Meld parece muito melhor, mas é extremamente lento para arquivos do tipo CSV. Eu achei o Diffuse a ferramenta de difusão Linux mais rápida.
Dan Dascalescu

@ DanDascalescu: Uma ferramenta que faz o trabalho parece sempre bem, independentemente da idade. Outro, que eu usei ocasionalmente, mas não está instalado para testá-lo com dados longos da coluna, é o tkdiff .
usuário desconhecido

O xxdiff exibe linhas movidas ? Ou mostra apenas uma linha ausente em um arquivo e uma adicionada no outro? (Tentei criar o xxdiff, mas o qmake falhou e vejo que eles não se preocupam em publicar um pacote Debian).
Dan Dascalescu

@ DanDascalescu: Hoje, só tenho o tkdiff instalado.
usuário desconhecido

1

Na linha de comando, certifique-se de adicionar novas linhas criteriosas antes de comparar arquivos. Você pode usar sed, awk, perl ou qualquer outra coisa para adicionar quebras de linha de alguma forma sistemática - certifique-se de não adicionar muitas.

Mas acho que o melhor é usar o vim, pois destaca as diferenças de palavras. O vim é bom se não houver muitas diferenças e as diferenças forem simples.


Embora não seja realmente uma resposta para a pergunta, essa técnica é bastante eficiente para aprender sobre pequenas diferenças em longas filas.
jknappen

1

O kdiff3 está se tornando o visualizador padrão da GUI no Linux. É semelhante ao xxdiff , mas acho que o kdiff3 é melhor. Ele faz muitas coisas bem, incluindo sua solicitação para mostrar "diferenças exatas de caracteres entre duas linhas em determinados arquivos".


O KDiff3 é extremamente lento para destacar as diferenças embutidas nos arquivos CSV. Eu não recomendaria.
Dan Dascalescu

1

Se estou lendo sua pergunta corretamente, eu uso diff -ypara esse tipo de coisa.

Torna a comparação de uma comparação lado a lado muito mais simples para descobrir quais linhas estão apresentando as diferenças.


11
Isso não destaca a diferença dentro da linha. Se você tem uma longa fila, é doloroso ver a diferença. wdiff, git diff --word-diff, vimgit, meld, kbdiff3, tkdiff todos fazem isso.
user2707671

1

Eu tive o mesmo problema e resolvi-o com o PHP Fine Diff , uma ferramenta on-line que permite especificar granularidade. Eu sei que não é tecnicamente uma ferramenta * nix, mas eu realmente não queria baixar um programa apenas para fazer uma comparação única de nível de caractere.


Alguns usuários não podem fazer upload de arquivos grandes ou sensíveis em uma ferramenta online aleatória. Existem muitas ferramentas que mostram diferenças no nível da linha sem comprometer sua privacidade.
Dan Dascalescu

Sim, existem. Porém, para diferenças que não contêm informações confidenciais, as ferramentas online podem ser uma boa solução.
pillravi

As ferramentas diff online também não oferecem suporte à integração da linha de comando. Você não pode usá-los no seu fluxo de controle de versão. Eles também são muito mais difíceis de usar (selecione o arquivo 1, selecione o arquivo 2, faça o upload) e não podem ser mesclados.
Dan Dascalescu
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.