1
votes

fusionner deux fichiers ini par leur nom de bloc

Comment fusionner deux blocs de deux fichiers ini?

Bonjour, j'ai deux fichiers ini qui stockent les données dans des blocs comme ci-dessous:

awk 'NR==FNR{a[$0]=$0;next} $0 in a{print}' f1 f2
[default]

[foo]

awk -vRS='' '{$1=$1}1' f1 f2 |awk '!a[$1]++'
[default] a1=1 b1=2 c1=3
[foo] d=1 e1=5
[bar] f2=10

Je dois fusionner ces deux fichiers comme suit:

[default]
a1=1
b1=2
c1=3
a2=5
b2=6

[foo]
d=1
e1=5
c2=7
d2=8
e2=9


[bar]
f2=10

Honnêtement, je ne sais pas depuis où commencer et quelle logique est nécessaire ou l'outil.

Certaines des choses stupides que j'ai essayées pour obtenir les directions sont:

 -->cat f1
[default]
a1=1
b1=2
c1=3

[foo]
d=1
e1=5


 -->cat f2
[default]
a2=5
b2=6

[foo]
c2=7
d2=8
e2=9

[bar]
f2=10


4 commentaires

Que faire si les fichiers contiennent la même clé mais avec des valeurs différentes? Qu'en est-il des lignes commentées (le cas échéant).


f2 doit écraser la clé de f1 en cas de dups. Les commentaires en double sont ok, je peux vivre avec ça.


Vous ne direz pas que le jour où un commentaire tentera de vous poignarder pendant que vous dormez.


mais mon exigence semble si difficile que je ne peux pas penser aux commentaires du moins pour le moment


4 Réponses :


2
votes

Une solution Perl consisterait à utiliser un analyseur INI tel que Config :: Tiny pour lire chacun, fusionner la structure de données résultante et écrire un nouveau fichier. Notez que cela ne préserve pas les commentaires ni l'ordre (pour ce dernier, vous pouvez utiliser Config :: Tiny :: Ordonné , mais la fusion est plus difficile).

use strict;
use warnings;
use Config::Tiny;

my $config1 = Config::Tiny->read('f1');
my $config2 = Config::Tiny->read('f2');
foreach my $category (keys %$config2) {
  my $section1 = $config1->{$category} //= {};
  my $section2 = $config2->{$category};
  @$section1{keys %$section2} = values %$section2;
}
$config1->write('new');


3 commentaires

merci pour l'aide, bien que je n'ai pas ce module installé dans le système. apprécier ton aide


Config :: Tiny est pur-perl et peut donc être fatpacked dans un script.


Vous devez bien sûr installer App :: FatPacker pour ce faire, mais le processus de base serait: installer App :: cpanminus ou le télécharger à partir de cpanmin.us ; cpanm -l local Config :: Tiny ; fatpack tree $ (env PERL5LIB = "$ PWD / local / lib / perl5" fatpack packlists-pour Config / Tiny.pm) ; fichier fatpack script.pl> script.packed.pl



4
votes

En utilisant awk , vous pouvez faire ceci:

[default]
a1=1
b1=2
c1=3
a2=5
b2=6

[foo]
d=1
e1=5
c2=7
d2=8
e2=9

[bar]
f2=10

awk '/^$/{
   next
}
/^\[.*\]$/{
   hdr = $0
   next
}
a[hdr] != "" {
   a[hdr] = a[hdr] ORS $0
   next
}
{
   a[hdr] = $0
   seq[++n] = hdr
}
END {
   for (i=1; i<=n; i++)
      print seq[i] ORS a[seq[i]] (i<n ? ORS : "")
}' f1 f2

Détails: fort>

  • / ^ $ / correspond à toutes les lignes vides que nous ignorons simplement
  • /^\[.*\ $/ noms d'en-tête correspondants que nous stockons dans la variable hdr
  • a [hdr]! = "" {...} lorsque nous avons déjà traité hdr une fois, nous ajoutons une nouvelle ligne et la ligne courante dans le tableau a indexé par hdr
  • Sinon, nous stockons simplement la ligne courante dans le tableau a indexé par hdr . Nous stockons également hdr dans un autre tableau seq indexé par incrémentation du nombre pour imprimer les données dans l'ordre
  • Dans le bloc END , nous parcourons le tableau seq et imprimons chaque bloc d'en-tête et de détails. Nous ajoutons une nouvelle ligne si nous avons plus de données à traiter.


7 commentaires

merci, pouvez-vous s'il vous plaît expliquer en haut niveau afin que je puisse suivre


Bien sûr, je vais ajouter plus de détails en réponse.


ord est le nom d'une fonction dans certains awks et dans l'extension ordchr gawk. Utilisez un nom différent pour la baie pour la portabilité.


Ah merci Ed. Je ne savais pas que ord était une fonction.


Ouais, ord () renvoie le nombre ordinal d'un caractère. Voir gnu.org/software/gawk/manual/gawk .html # Extension-Sample-Ord .


salut Anubhava, y a-t-il un moyen d'imprimer des commentaires dans la sortie de tout le fichier ou au moins du premier fichier? J'ai essayé d'ajouter / ^ # / {print; next} mais ça gâche toute la sortie


Désolé, je n'ai pas compris cette nouvelle exigence. Veuillez modifier la question et je verrai s'il est possible de modifier cette solution



1
votes
$ cat tst.awk
BEGIN { RS=""; ORS="\n\n"; FS=OFS="\n" }
{ key = $1 }
NR == FNR { rec[key] = $0; next }
key in rec { $1 = rec[key]; delete rec[key] }
{ print }
END {
    for (key in rec) {
        print rec[key]
    }
}

$ awk -f tst.awk file1 file2
[default]
a1=1
b1=2
c1=3
a2=5
b2=6

[foo]
d=1
e1=5
c2=7
d2=8
e2=9

[bar]
f2=10

0 commentaires

0
votes

Cela pourrait fonctionner pour vous (GNU diff & sed):

diff -au999 file1 file2 | sed '1,3d;s/.//' >file3

Utilisé diff -u999 pour unifier file1 et fil2, puis supprimez les 3 lignes d'en-tête et le premier caractère de chaque ligne.

NB Si fichier1 et fichier2 sont identiques, vous n'obtiendrez aucune sortie et les mêmes clés seront répétées si elles ont des valeurs différentes.


0 commentaires