como usar patch e diff para mesclar dois arquivos

15

Eu li sobre diff e patch, mas não consigo descobrir como aplicar o que preciso. Eu acho que é bem simples, então, para mostrar meu problema, pegue esses dois arquivos:

a.xml

<resources>
   <color name="same_in_b">#AAABBB</color>
   <color name="not_in_b">#AAAAAA</color>
   <color name="in_b_but_different_val">#AAAAAA</color>
   <color name="not_in_b_too">#AAAAAA</color>
</resources>

b.xml

<resources>
   <color name="same_in_b">#AAABBB</color>
   <color name="in_b_but_different_val">#BBBBBB</color>
   <color name="not_in_a">#AAAAAA</color>
</resources>

Eu quero ter uma saída, que se parece com isso (a ordem não importa):

<resources>
   <color name="same_in_b">#AAABBB</color>
   <color name="not_in_b">#AAAAAA</color>
   <color name="in_b_but_different_val">#BBBBBB</color>
   <color name="not_in_b_too">#AAAAAA</color>
   <color name="not_in_a">#AAAAAA</color>
</resources>

A mesclagem deve conter todas as linhas ao longo dessas regras simples:

  1. qualquer linha que esteja apenas em um dos arquivos
  2. se uma linha tiver a mesma tag de nome, mas um valor diferente, pegue o valor da segunda

Eu quero aplicar esta tarefa dentro de um script bash, por isso não deve necessariamente ser feito com diff e patch, se outro programa for um ajuste melhor

    
por Rafael T 02.02.2013 / 02:00

7 respostas

20

Você não precisa de patch para isso; é para extrair alterações e enviá-las sem a parte inalterada do arquivo.

A ferramenta para mesclar duas versões de um arquivo é merge , mas como @vonbrand escreveu, você precisa do arquivo "base" do qual suas duas versões divergiram. Para fazer uma mesclagem sem ele, use diff da seguinte forma:

diff -DVERSION1 file1.xml file2.xml > merged.xml

Ele incluirá cada conjunto de alterações nos comandos em estilo C #ifdef / #ifndef "pré-processador", desta forma:

#ifdef VERSION1
<stuff added to file1.xml>
#endif
...
#ifndef VERSION1
<stuff added to file2.xml>
#endif

Se uma linha ou região diferir entre os dois arquivos, você receberá um "conflito", que se parece com isso:

#ifndef VERSION1
<version 1>
#else /* VERSION1 */
<version 2>
#endif /* VERSION1 */

Portanto, salve a saída em um arquivo e abra-a em um editor. Procure por lugares onde #else apareça e resolva-os manualmente. Em seguida, salve o arquivo e execute-o em grep -v para se livrar das% restantes#if(n)def e #endif lines:

grep -v '^#if' merged.xml | grep -v '^#endif' > clean.xml

No futuro, salve a versão original do arquivo. merge pode dar a você resultados muito melhores com a ajuda da informação extra. (Mas tenha cuidado: merge edita um dos arquivos no local, a menos que você use -p . Leia o manual).

    
por 02.02.2013 / 13:23
5

merge(1) provavelmente está mais próximo do que você deseja, mas isso requer um ancestral comum aos seus dois arquivos.

A maneira (suja!) de fazer isso é:

  1. Livre-se da primeira e da última linha, use grep(1) para excluí-las
  2. Esmague os resultados juntos
  3. sort -u deixa uma lista ordenada, elimina duplicatas
  4. Substituir primeira / última linha

Humm ... algo ao longo das linhas:

echo '<resources>'; grep -v resources file1 file2 | sort -u; echo '</resources>'

pode fazer.

    
por 02.02.2013 / 02:29
3

sdiff (1) - mesclagem lado a lado das diferenças de arquivo

Use a opção --output , isso interativamente mesclará quaisquer dois arquivos. Você usa comandos simples para selecione uma alteração ou edite uma alteração.

Você deve verificar se a variável de ambiente EDITOR está definida. O editor padrão para comandos como "eb" geralmente é ed , um editor de linha .

EDITOR=nano sdiff -o merged.txt file1.txt file2.txt
    
por 19.04.2018 / 07:28
1

Aqui está uma solução simples que funciona fundindo até 10 arquivos :

#!/bin/bash

strip(){
    i=0
    for f; do
        sed -r '
            /<\/?resources>/ d
            s/>/>'$((i++))'/
        ' "$f"
    done
}

strip "$@" | sort -u -k1,1 -t'>' | sed '
    1 s|^|<resources>\n|
    s/>[0-9]/>/
    $ a </resources>
'

por favor note que o argumento que vem primeiro tem a precedência então você tem que chamar:

script b.xml a.xml

para obter valores comuns mantidos em b.xml em vez de a.xml .

script b.xml a.xml outs:

<resources>
   <color name="in_b_but_different_val">#BBBBBB</color>
   <color name="not_in_a">#AAAAAA</color>
   <color name="not_in_b">#AAAAAA</color>
   <color name="not_in_b_too">#AAAAAA</color>
   <color name="same_in_b">#AAABBB</color>
</resources>
    
por 02.02.2013 / 11:39
1

Outro hack horrível - poderia ser simplificado, mas: P

#!/bin/bash

i=0

while read line
do
    if [ "${line:0:13}" == '<color name="' ]
    then
        a_keys[$i]="${line:13}"
        a_keys[$i]="${a_keys[$i]%%\"*}"
        a_values[$i]="$line"
        i=$((i+1))
    fi
done < a.xml

i=0

while read line
do
    if [ "${line:0:13}" == '<color name="' ]
    then
        b_keys[$i]="${line:13}"
        b_keys[$i]="${b_keys[$i]%%\"*}"
        b_values[$i]="$line"
        i=$((i+1))
    fi
done < b.xml

echo "<resources>"

i=0

for akey in "${a_keys[@]}"
do
    print=1

    for bkey in "${b_keys[@]}"
    do
        if [ "$akey" == "$bkey" ]
        then
            print=0
            break
        fi
    done

    if [ $print == 1 ]
    then
        echo "  ${a_values[$i]}"
    fi

    i=$(($i+1))
done

for value in "${b_values[@]}"
do
    echo "  $value"
done

echo "</resources>"
    
por 02.02.2013 / 13:31
0

OK, segunda tentativa, agora em Perl ( não qualidade de produção, sem verificação!):

#!/usr/bin/perl

open(A, "a.xml");

while(<A>) {
  next if(m;^\<resource\>$;);
  next if(m;^\<\/resource\>$;);
  ($name, $value) = m;^\s*\<color\s+name\s*\=\s*\"([^"]+)\"\>([^<]+)\<\/color\>$;;
  $nv{$name} = $value if $name;
}

close(A);

open(B, "b.xml");

while(<B>) {
  next if(m;^\<resource\>$;);
  next if(m;^\<\/resource\>$;);
  ($name, $value) = m;^\s*\<color\s+name\s*\=\*\"([^"]+)\"\>([^<]+)\<\/color\>$;;
  $nv{$name} = $value if $name;
}

close(B);

print "<resource>\n";
foreach (keys(%nv)) {
    print "   <color name=\"$_\">$nv{$_}</color>\n";
}
print "</resource>\n";
    
por 02.02.2013 / 03:47
0

Outro, usando cut e grep ... (toma a.xml b.xml como argumentos)

#!/bin/bash

zap='"('"'grep '<color' "$2" | cut -d '"' -f 2 | tr '\n' '|''"'")'
echo "<resources>"
grep '<color' "$1" | grep -E -v "$zap"
grep '<color' "$2"
echo "</resources>"
    
por 02.02.2013 / 13:42