Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Finding the parent of a text in a file

by ExperimentsWithPerl (Acolyte)
on Sep 04, 2015 at 10:04 UTC ( [id://1140982]=perlquestion: print w/replies, xml ) Need Help??

ExperimentsWithPerl has asked for the wisdom of the Perl Monks concerning the following question:

Hello Perl Monks,
I am utterly new to perl(Just a week old).I am trying to write a program which would compare two configs file and print the difference (wherever found) and also indicating the parents of the differing text.For ex,if my file is
    system
         security
             management-ip
                 protocol
                        internal
                        shutdown
                        allow
                        entry 1
                            snmp ok
                        exit



Now suppose the files are differening at "snmp ok"
so I want the code to print

    system
         security
             management-ip
                 protocol
                        entry 1
                            snmp ok
and not the in between ( internal shutdown allow ) as all these 3 have the same parent as entry 1.
Please help.

Replies are listed 'Best First'.
Re: Finding the parent of a text in a file
by choroba (Cardinal) on Sep 04, 2015 at 14:42 UTC
    You can transform the input lines into "full paths", i.e. include all the antecendents to each element. Then, you can just run standard diff (Algorithm::Diff) on the items:
    #!/usr/bin/perl use warnings; use strict; use Algorithm::Diff qw{ diff }; open my $F1, '<', '1.in' or die $!; my @lines1 = <$F1>; open my $F2, '<', '2.in' or die $!; my @lines2 = <$F2>; chomp @lines1; chomp @lines2; # Transform the input lines. my @path = q(); my @indent = q(); for my $line (@lines1, @lines2) { my ($this_indent, $key) = $line =~ /^( +)(.*)/; if ($this_indent eq $indent[-1]) { $path[-1] = $key } else { if ($this_indent le $indent[-1]) { pop @indent, pop @path while $indent[-1] ge $this_indent; } push @path, $key; push @indent, $this_indent; } $line = join '/', @path; } # Compare the lines. my @diffs = diff(\@lines1, \@lines2); for my $diff (@diffs) { for my $line (@$diff) { print "@$line\n"; } }
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

      Thank you Choroba.This indeed looks useful.
      However this code is not able to proceed beyond
      $line = join '/', @path;
      (just before # Compare the lines)
      Its getting stuck there.

        I ran it on the input you provided, I changed the 2nd file a bit more and got the following result:
        - 5 /system/security/management-ip/protocol/shutdown + 5 /system/security/management-ip/protocol/reboot - 7 /system/security/management-ip/protocol/entry 1 - 8 /system/security/management-ip/protocol/entry 1/snmp ok + 7 /system/security/management-ip/protocol/entry 2 + 8 /system/security/management-ip/protocol/entry 2/snmp ok
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Finding the parent of a text in a file ( is_deeply )
by 1nickt (Canon) on Sep 04, 2015 at 12:27 UTC

    You can use is_deeply if you put your data into hashes. I'm making some assumptions here because your post isn't complete (eg that 'entry 1' and 'snmp ok' are keys, not key-value pairs) ... but hopefully this helps.

    #!/usr/bin/perl use strict; use warnings; use Test::More tests => 1; my ($href1, $href2) = get_data(); is_deeply( $href2, $href1, 'Hashref comparison' ); ## Sub to fetch data below; add code to read files if needed sub get_data { my $href1 = { system => { security => { management_ip => { protocol => { internal => {}, shutdown => {}, allow => {}, entry_1 => { snmp_ok => 'foo', }, exit => {}, }, }, }, }, }; my $href2 = { system => { security => { management_ip => { protocol => { internal => {}, shutdown => {}, allow => {}, entry_1 => { snmp_ok => 'bar', }, exit => {}, }, }, }, }, }; return $href1, $href2; } __END__
    Output:
    $ perl 1140982.pl 1..1 not ok 1 - Hashref comparison # Failed test 'Hashref comparison' # at 1140982.pl line 8. # Structures begin differing at: # $got->{system}{security}{management_ip}{protocol}{entry_1}{ +snmp_ok} = 'bar' # $expected->{system}{security}{management_ip}{protocol}{entry_1}{ +snmp_ok} = 'foo' # Looks like you failed 1 test of 1. $

    The way forward always starts with a minimal test.

      Hey 1nickt,

      I recently ran into an issue while updating a module where I needed the functionality of is_deeply(), but I didn't want any test modules in the code, and I didn't want to have to hijack the output with trickery.

      I came across Data::Compare, which simply returns a true/false bool if the structs match. You may be interested to play with this module for this kind of non-test work.

      -stevieb

        Thanks. Looks useful. I like Test::More because it's a core module, so every installation has it, and because it indicates where the data differs (begins to differ). Usually I use this type of comparison where if it fails it should be fatal, so I don't mind the output. I can see how Data::Compare would be a better choice if you wanted to continue anyway. Interestingly, the same author has Test::Differences, which goes in the other direction (is still a Test::, spits TAP to STDOUT, provides a comprehensive diff of the structures).

        The way forward always starts with a minimal test.

      Thanks lnickt for your help.
      This program indeed does work But the issue is I have 2 very huge files to compare and they can have any hierarchy apart from system/security/entry etc (thus I cannot hard code the keys/key-value in the program).
      Also, In this case the difference was in the child of entry 1, similarly in my file the difference can be at the entry 229 (say) , then it would become hard for me to go back till parent and print the same.

        Hi ExperimentsWithPerl,

        The data structures were only hard-coded in my example because it was an example. You would need to create the hashes on the fly from the files, and then compare them.

        If you are wanting to know all the differences in the files, you could use Test::Differences::eq_or_diff_data(), but that still wouldn't print out just the hierarchy as you wish. For that, choroba's solution seems like it may be the way to go.

        But any diff solution (Test::Differences, system diff, whatever) will specify the lines at which the structures differ, so if that's all you need to know it might not be necessary to make the fully qualified "paths" choroba suggested. (It would help if you provided a sample input file and an example of the actual output you'd like to see.)

        The way forward always starts with a minimal test.
Re: Finding the parent of a text in a file
by kroach (Pilgrim) on Sep 04, 2015 at 12:07 UTC
    What code do you already have? What exact problem prevents you from completing this program?
Re: Finding the parent of a text in a file
by Anonymous Monk on Sep 04, 2015 at 17:15 UTC

    Seems to do what you showed. If not, please say why and provide a more extensive test case.

    #!/usr/bin/perl # http://perlmonks.org/?node_id=1140982 use Algorithm::Diff qw(traverse_sequences); use strict; use warnings; my $file1 = <<END; system security management-ip protocol internal shutdown allow entry 1 snmp ok exit END my $file2 = <<END; system security management-ip protocol internal shutdown allow entry 1 snmp failed exit END sub fullpath { my @full; my @answer; for my $line ( shift() =~ /.+\n/g ) { $line =~ /^( *)/; $#full = length($1) - 1; # truncate array push @full, $line; push @answer, join '', grep defined, @full; } return \@answer; } my $full1 = fullpath($file1); my $full2 = fullpath($file2); traverse_sequences( $full1, $full2, { DISCARD_A => sub {print $full1->[shift()], "\n"}, } );

      thanks a lot Anonymous Monk.You are right, it works fine with the example I have given .
      But suppose I add another line below "snmp ok" (at the same level) :-

      ipaddress#1

      Then the code is printing the hirarechy twice for me ..once till "snmp ok" and once till "ipaddress#1".

      Second thing, I tried to open both the config files as a file handler (Becase in real world the files will be too huge to keep in the code) and tried to run the code (with minimal changes), the code didnt work .

        Ah, you changed your requirements.

        You get what you ask for, we're not mindreaders. How are we supposed to figure out what you really want when you don't give examples?

        You never did mention file sizes, did you?

        Saying "(with minimal changes), the code didnt work " without showing what the changes were and why the code didn't work is an insult to all programmers everywhere.

        You owe me an apology.

        You also owe me test cases for the change of requirements you made.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1140982]
Approved by planetscape
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (8)
As of 2024-04-23 10:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found