Tête de fichier de diff

11

J'ai deux fichiers. Un fichier, je suppose, est un sous-ensemble de l'autre. Existe-t-il un moyen de différencier les fichiers pour identifier (de manière succincte) où dans le premier fichier le deuxième fichier tient?

Richard
la source
Voulez-vous dire que les lignes d'un fichier sont une sous-séquence de l'autre, ou en fait une sous-chaîne contiguë?
Kaz
Une sous-chaîne contiguë, @Kaz.
Richard

Réponses:

14

diff -e bigger smaller fera l'affaire, mais nécessite une certaine interprétation, car la sortie est un "script ed valide".

J'ai fait deux fichiers, "plus gros" et "plus petit", où le contenu de "plus petit" est identique aux lignes 5 à 9 de "plus grand" faisant "diff -e plus grand plus petit" m'a obtenu:

% diff -e bigger smaller
10,15d
1,4d

Ce qui signifie "supprimer les lignes 10 à 15 de" plus grandes ", puis supprimer les lignes 1 à 4, pour devenir" plus petites "". Cela signifie que "plus petit" correspond aux lignes 5 à 9 de "plus grand".

Inverser les noms de fichiers m'a rendu quelque chose de plus compliqué. Si «plus petit» constitue vraiment un sous-ensemble de «plus grand», seules les commandes «d» (pour suppression) apparaîtront dans la sortie.

Bruce Ediger
la source
5

Vous pouvez le faire visuellement avec meld . Malheureusement, c'est un outil GUI mais si vous ne voulez le faire qu'une seule fois, et sur un fichier relativement petit, ça devrait aller:

L'image ci-dessous est la sortie de meld a b:

entrez la description de l'image ici

terdon
la source
1
Meld est sympa, mais il ne joue pas aussi bien avec des fichiers de 100 Mo et plus.
Richard
@Richard non ça ne marche pas et je préférerais quand même un outil en ligne de commande, je pensais juste le mentionner.
terdon
Ressemble beaucoup à vimdiff, qui est disponible dans le terminal.
Patrick
2

Si les fichiers sont suffisamment petits, vous pouvez les extraire tous les deux dans Perl et demander à son moteur regex de faire l'affaire:

perl -0777e '
        open "$FILE1","<","file_1";
        open "$FILE2","<","file_2";
        $file_1 = <$FILE1>;
        $file_2 = <$FILE2>;
        print "file_2 is", $file_1 =~ /\Q$file_2\E/ ? "" : "not";
        print " a subset of file_1\n";
'

Le -0777commutateur demande à Perl de définir son séparateur d'enregistrements d'entrée $/sur la valeur indéfinie afin de slurper complètement les fichiers.

Joseph R.
la source
1
Que fait 777-il? Je suppose que vous passez NULL $/mais pourquoi? De plus, comme ce sont des commutateurs un peu ésotériques, une explication serait bien pour les non-perl.
terdon
1
@terdon Je le fais en effet pour slurper les fichiers entiers. Explication ajoutée.
Joseph R.
Mais pourquoi est-ce nécessaire? $a=<$fh>devrait slurp de toute façon à droite?
terdon
1
@terdon Pas que je sache, non. Par défaut, il $/est défini sur \npour que la $a=<$fh>lecture d'une seule ligne du fichier $fhsoit ouverte. À moins bien sûr que perlle comportement de la ligne de commande ait des valeurs par défaut différentes que je ne connais pas?
Joseph R.
Argh, oui, mon mauvais, je n'ai presque jamais slurpé de fichiers ou utilisé l' while $foo=<FILE>idiome, donc je n'étais pas sûr et j'ai exécuté un (mauvais) test qui semblait fonctionner. Ça ne fait rien :).
terdon
1

Si les fichiers sont des fichiers texte et smaller, dans biggercommence au début d'une ligne, ce n'est pas trop difficile à implémenter avec awk:

awk -v i=0 'NR==FNR{l[n++]=$0;next}
    {if ($0 == l[i]) {if (++i == n) {print FNR-n+1;exit}} else i=0}
    ' smaller bigger
Stéphane Chazelas
la source
1

Votre question est "Tête de fichier diff". Si vous voulez vraiment dire qu'un fichier est la tête de l'autre, alors un simple cmpvous dira que:

cmp big_file small_file
cmp: EOF on small_file

Cela vous indique qu'une différence entre les deux fichiers n'a pas été détectée avant la fin du fichier lors de la lecture small_file.

Si toutefois vous voulez dire que l'intégralité du texte d'un petit fichier peut apparaître n'importe où à l'intérieur big_file, alors en supposant que vous puissiez tenir les deux fichiers en mémoire, vous pouvez utiliser

perl -le '
   use autodie;
   undef $/;
   open SMALL, "<", "small_file";
   open BIG, "<", "big_file";
   $small = <SMALL>;
   $big = <BIG>;
   $pos = index $big, $small;
   print $pos if $pos >= 0;
'

Cela imprimera le décalage à l'intérieur big_filedu contenu de small_file(par exemple 0 si small_filecorrespond au début de big_file). Si small_filene correspond pas à l'intérieur big_file, rien ne sera imprimé. S'il y a une erreur, l'état de sortie sera différent de zéro.

jrw32982 prend en charge Monica
la source