iki dosyayı birleştirmek ve çakışmaları otomatik olarak çözmek için yama ve diff nasıl kullanılır


19

Fark ve yama hakkında okudum ama ihtiyacım olan şeyi nasıl uygulayacağımı anlayamıyorum. Sanırım oldukça basit, bu yüzden sorunumu göstermek için bu iki dosyayı al:

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>

Ben böyle bir çıktı var istiyorum (sipariş önemli değil):

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

Birleştirme, bu basit kurallar boyunca tüm satırları içermelidir:

  1. yalnızca dosyalardan birinde olan herhangi bir satır
  2. bir satır aynı ad etiketine ancak farklı bir değere sahipse, değeri ikinci

Bu görevi bir bash betiği içinde uygulamak istiyorum, bu yüzden başka bir program daha uygunsa, zorunlu olarak diff ve patch ile yapılması gerekmemelidir


diffhangi satırların bir dosyada olduğunu ancak diğerinin olmadığını ancak tüm satırların ayrıntı düzeyinde olduğunu söyleyebilir. patchyalnızca benzer bir dosyada aynı değişiklikleri yapmak için uygundur (belki de aynı dosyanın farklı bir sürümü veya her değişiklik için satır numaralarının ve çevresindeki satırların orijinal dosyanızla aynı olduğu tamamen farklı bir dosya). Yani hayır, bu görev için özellikle uygun değiller. Bir göz atmak isteyebilirsiniz, wdiffancak çözüm muhtemelen özel bir komut dosyası gerektirir. Verileriniz XML gibi göründüğünden, bazı XSL araçlarını aramak isteyebilirsiniz.
tripleee

1
Özel komut dosyalarıyla neden tüm yanıtlar? Birleştirme standart ve karmaşık bir sorundur ve bunun için iyi araçlar vardır. Tekerleği yeniden icat etme.
alexis

Yanıtlar:


23

Buna ihtiyacınız yok patch; değişiklikleri ayıklamak ve dosyanın değişmeyen kısmı olmadan göndermek içindir.

Bir dosyanın iki sürümünü birleştirme aracıdır merge, ancak @vonbrandyazıldığı gibi, iki sürümünüzün birbirinden ayrıldığı "temel" dosyaya ihtiyacınız vardır. Onsuz birleştirme yapmak için diffşu şekilde kullanın :

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

C tarzı #ifdef/ #ifndef"önişlemci" komutlarındaki her bir değişiklik kümesini şu şekilde kapsayacaktır :

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

Bir çizgi veya bölge iki dosya arasında farklıysa, şuna benzer bir "çakışma" alırsınız:

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

Çıktıyı bir dosyaya kaydedin ve bir düzenleyicide açın. Gelen yerleri arayın #elseve manuel olarak çözün. Sonra dosyayı kaydedin ve grep -vkalan #if(n)defve #endifsatırlardan kurtulmak için çalıştırın :

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

Gelecekte, dosyanın orijinal sürümünü kaydedin. mergeekstra bilgiler sayesinde size çok daha iyi sonuçlar verebilir. (Ancak dikkatli olun: mergekullanmadığınız sürece dosyalardan birini yerinde düzenler -p. Kılavuzu okuyun).


Bir çatışmam olsaydı bir şey ekledimsed -e "s/^#else.*$/\/\/ conflict/g"
lockwobr

1
Bunun iyi bir fikir olduğunu sanmıyorum. Cevabımda yazdığım gibi #else, çatışma çözümü sırasında satırları editörde manuel olarak kaldırmalısınız .
alexis

6

merge(1) muhtemelen istediğiniz şeye daha yakındır, ancak bu, iki dosyanız için ortak bir ata gerektirir.

Bunu yapmanın (kirli!) Bir yolu:

  1. İlk ve son satırlardan kurtulun, grep(1)bunları hariç tutmak için kullanın
  2. Sonuçları birlikte parçalayın
  3. sort -u sıralanmış bir liste bırakır, kopyaları ortadan kaldırır
  4. İlk / son satırı değiştir

Humm ... çizgiler boyunca bir şey:

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

yapabilir.


bu özel örnekte çalışır, ancak genel olarak DEĞİLDİR: Bir sıralama name in_b_but_different_valdeğeri varsa #00AABBbunu üstüne koyacak ve birincisi yerine ikinci değeri silecek
Rafael T

bu durumda en uygun çözüm için, XML'yi yukarıdaki kesmekle değil gerçek bir XML ayrıştırıcıyla ayrıştırmanız ve bundan yeni bir birleştirilmiş XML çıktısı oluşturmanız gerekir. diff / patch / sort vs. sadece "belirli örneklere" özel tasarlanmış tüm
hacklerdir

@alzheimer, bize göstermek için basit bir şey kırbaç ...
vonbrand

Görünüşe göre diff3aynı şekilde çalışıyor. Ortak bir ata dosyası gerektiriyor. Neden sadece 2 dosyayı birlikte diffgösterenlere göre birleştiren basit bir CLI aracı yok .
CMCDragonkai

5

sdiff (1) - dosya farklılıklarının yan yana birleştirilmesi

--outputSeçeneği kullanın , bu iki dosyayı etkileşimli olarak birleştirir. Bir değişiklik seçmek veya değişikliği düzenlemek için basit komutları kullanırsınız.

EDITOROrtam değişkeninin ayarlandığından emin olmalısınız . "Eb" gibi komutlar için varsayılan düzenleyici genellikle edbir satır düzenleyicidir .

EDITOR=nano sdiff -o merged.txt file1.txt file2.txt

1
vimEDITOR olarak kullanmayı daha iyi buluyorum . Ama bu en iyi çözüm, diffkomutla birlikte geliyor !
CMCDragonkai

1

İşte 10 dosyaya kadar birleştirmeye çalışan basit bir çözüm :

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

lütfen önce gelen argümanın önceliğe sahip olduğunu unutmayın, bu nedenle şunu aramanız gerekir:

script b.xml a.xml

ortak değerleri korumak b.xmlyerine a.xml.

script b.xml a.xml çıkışları:

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

1

Başka bir korkunç kesmek - basitleştirilebilir, ancak: 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>"

0

Tamam, ikinci deneme, şimdi Perl'de ( üretim kalitesi değil , kontrol yok!):

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

0

Bir diğeri, cut ve grep kullanarak ... (a.xml b.xml'yi bağımsız değişken olarak alır)

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

echovarsayılan eylemdir, bu yüzden xargs echogereksizdir. Neden tr '\n' '|'yine de yapmıyorsun ?
tripleee

İyi bir nokta - bu sadece hızlı bir kesmek. Ben düzenleyeceğim.
frostschutz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.