comment utiliser patch et diff pour merge deux files

J'ai lu sur diff et patch, mais je ne peux pas comprendre comment appliquer ce dont j'ai besoin. Je suppose que c'est assez simple, donc pour montrer mon problème, prenons ces deux files:

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> 

Je veux avoir une sortie, qui ressemble à ceci (l'ordre n'a pas d'importance):

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

La fusion devrait contenir toutes les lignes le long de ces règles simples:

  1. toute ligne qui n'est que dans l'un des files
  2. si une ligne a la même label de nom mais une valeur différente, prenez la valeur de la deuxième

Je veux appliquer cette tâche à l'intérieur d'un script bash, donc il ne faut pas nécessairement avoir fini avec diff et patch, si un autre programme est meilleur.

Vous n'avez pas besoin de patch pour cela; c'est pour extraire les modifications et les envoyer sans la partie inchangée du file.

L'outil de fusion de deux versions d'un file est la merge , mais comme @vonbrand écrit, vous avez besoin du file "base" à partir duquel vos deux versions ont divergé. Pour faire une fusion sans cela, utilisez diff comme ceci:

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

Il encapsulera chaque set de modifications dans les commands " #ifdef type C #ifdef / #ifndef , comme ceci:

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

Si une ligne ou une région diffère entre les deux files, vous obtenez un «conflit» qui ressemble à ceci:

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

Enregistrez donc la sortie dans un file et ouvrez-la dans un éditeur. Recherchez les endroits où #else apparaît et résolvez-les manuellement. Sauvegardez ensuite le file et exécutez-le via grep -v pour supprimer les lignes #if(n)def et #endif restantes:

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

À l'avenir, sauvegardez la version originale du file. merge peut vous donner de bien meilleurs résultats avec l'aide de l'information supplémentaire. (Mais faites attention: merge un des files sur place, sauf si vous utilisez -p . Lisez le manuel).

merge(1) est probablement plus proche de ce que vous voulez, mais cela nécessite un ancêtre commun à vos deux files.

Une façon (sale!) De le faire est:

  1. Débarrassez-vous des premières et dernières lignes, utilisez grep(1) pour les exclure
  2. Smash les résultats set
  3. sort -u laisse une list sortingée, élimine les duplicates
  4. Remplacer la première / dernière ligne

Humm … quelque chose comme ça:

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

pourrait faire.

Voici une solution simple qui fonctionne en fusionnant jusqu'à 10 files :

 #!/bin/bash ssortingp(){ i=0 for f; do sed -r ' /<\/?resources>/ d s/>/>'$((i++))'/ ' "$f" done } ssortingp "$@" | sort -u -k1,1 -t'>' | sed ' 1 s|^|<resources>\n| s/>[0-9]/>/ $ a </resources> ' 

s'il vous plaît noter le arg qui vient en premier a la préséance si vous devez appeler:

 script b.xml a.xml 

pour get des valeurs communes conservées à partir de b.xml plutôt que 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> 

Un autre hack horrible – pourrait être simplifié, mais: 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>" 

Exploitez la puissance du XML canonique sortingé récursivement via xmllint, ce qui se traduit par une comparaison plus intelligente.

Comment puis-je différencier deux files XML?

OK, deuxième essai, maintenant en Perl ( pas de qualité de production, pas de vérification!):

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

Un autre, en utilisant couper et grep … (prend un.xml b.xml comme arguments)

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