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

Hi guys,

I have this ini file:

[performance] pop1 = 192.168.1.1 pop2 = 192.168.1.2 [pop1] 2 = 123 2 = 345 4 = 125 [pop2] 6 = 503 10 = 444

and I do this:

sub mklist() { my $config = "/home/portal/test/performance.conf"; my %ini; tie %ini, 'Config::IniFiles', ( -file => $config); push @hostnames, keys%{$ini{'performance'}}; push @ips, values%{$ini{'performance'}}; #print "Hosts: @hostnames\n\n"; #print "IPs: @ips\n\n"; for (my $i = 0; $i < @hostnames; $i++) { push @slots, keys%{$ini{'$hostnames[$i]'}}; ## Here something +is wrong... push @ifindices, values%{$ini{'$hostnames[$i]'}}; } print "Slots: @slots\n\n"; ## this prints nothing print "Indices: @ifindices\n\n" ## this prints nothing as well... }

what is wrong in my code and the arrays @slots & @ ifindices are empty?? thanks!

P.S. If I do this:

push @slots, keys%{$ini{'pop1'}};

I get: 2 4 instead of 2 2 4

Why?

Replies are listed 'Best First'.
Re: Ini files
by Sewi (Friar) on Sep 04, 2009 at 08:52 UTC
    You didn't say what is going wrong with your code.
    I suggest adding this after tie'ing the ini-file:
    use Data::Dumper; print Dumper(\%ini)."\n";

    And you have two typos:

    push @slots, keys%{$ini{'$hostnames[$i}'}}; push @ifindices, values%{$ini{'$hostnames[$i}'}};
  • The closing bracket for [ should be ] instead of }
  • The ' chars within $ini{} make Perl treat this as a string and I don't think that there is a [$hostnames[$i}] - block in your file :-) Just remove the ' there.

  • These things are usually very hard to find expecially if you're working on a piece of code for a longer time without breaks. Usually, I add a print in those cases and _copy&paste_ the variables which don't work there. If you re-type them, you may loose some typos and will get even more confused.

    Please remember, that keys() and values() don't return the same order of items, so you won't get any associations between slots and ifindices. Try

    for (keys(%{$ini{$hostnames[$i]}})) { push @slots,$_; push @ifindices,${$ini{$hostnames[$i]}}{$_}; }

    Last note:
    If you don't really need $i, you could advoid it:

    for (@hostnames) { print $_; }
    In this loop, $_ is the same as $hostnames[$_] in your loop. For nested loops:
    for my $Hostname (@hostnames) { print $Hostname; }

      this:

      for (keys(%{$ini{$hostnames[$i]}})) { push @slots,$_; push @ifindices,${$ini{$hostnames[$i]}}{$_}; }

      how can I use it? $i is the variable inside the for loop (my $i). What I am trying to do is:

      to associate slots with indices for every host (performance section, pop1 & pop2). The simplest way I thought was with an ini file (since I have worked before with ini files.)

      Thanks!

        $_ ist the default variable for loops, you don't need a special inside variable.
        If you got such a loop (and don't see any commands that could harm your computer), it's wise to
        for (keys(%{$ini{$hostnames[$i]}})) { print "Slot: $_\n"; push @slots,$_; print "IfIndex: ${$ini{$hostnames[$i]}}{$_}\n"; push @ifindices,${$ini{$hostnames[$i]}}{$_}; }
        This will show you what is happening.

        Let's split the lines: $hostname[$i] contains your hostname. This is used as a key for the %ini - hash. So we could simplify it for better understanding as

        for (keys(%{$Hostpart_from_INI})) {
        $Hostpart_from_INI contains a reference to what Config::INI found for this hostname (the one for the current loop run). It is a Hash-Reference, so we could also write:
        for (keys(%Host_Data_from_INI)) {
        Okay, now we're here with a simple Hash. Each loop run gets another key from the Hash %Host_Data_from_INI. If you got Problems with $_, we could add it:
        for $_ (keys(%Host_Data_from_INI)) {
        You should try the following for better understanding:
        perl -le 'for (1,2,3) { print $_; }'
        You could also add the $_ behind the for or you could use any other variable.
        Still with me? Okay, let's write your loop using the simplified form:
        for (keys(%Host_Data_from_INI)) { push @slots,$_; push @ifindices,$Host_Data_from_INI{$_}; }
        Remember: We changed nothing, the code is still the same. All we did was renaming things and solving references to show what Perl sees when executing the script.
Re: Ini files
by ig (Vicar) on Sep 04, 2009 at 10:04 UTC
    I get: 2 4 instead of 2 2 4

    A hash never has duplicate keys. Your ini file sets the same parameter twice: one setting overrides the other. After all, there are only two parameters in the [pop1] section: 2 and 4. So you get exactly what you set in the ini: two keys.

Re: Ini files
by Anonymous Monk on Sep 04, 2009 at 08:57 UTC
      Yes I forgot my $i. Now the syntax of the code is correct. What I want to do is to be able to assosiate slots with indices for every host using ini file format. Can you help me please, cause I am new to perl...
        Don't struggle with ini, use YAML
        #!/usr/bin/perl -- use strict; use warnings; use YAML; use Data::Dumper; sub printdump { print Dump($_[0]),"\n\n"; print Data::Dumper->new([$_[0]])->Indent(1)->Dump,"\n"; print "---------- $_[0] ----------\n"; } { my %ini; $ini{performance}{pop1}{ip} = q{192.168.1.1}; $ini{performance}{pop1}{slots} = [ {2 => 123}, {2 => 345}, {4 => 125}, ]; $ini{performance}{pop2} = {}; $ini{performance}{pop2}{ip} = q{192.168.1.2}; $ini{performance}{pop2}{slots} = [ {6 => 503}, {10 => 444}, ]; printdump(\%ini); } { my %ini; $ini{performance}{pop1}=[ q{192.168.1.1}, {2 => 123}, {2 => 345}, {4 => 125}, ]; $ini{performance}{pop2} = [ q{192.168.1.2}, {6 => 503}, {10 => 444}, ]; printdump(\%ini); } { my %ini; $ini{performance}{pop1}=[ q{192.168.1.1}, [2 => 123], [2 => 345], [4 => 125], ]; $ini{performance}{pop2} = [ q{192.168.1.2}, [6 => 503], [10 => 444], ]; printdump(\%ini); } { my %ini; $ini{performance}{pop1} = [qw[ 192.168.1.1 2 123 2 345 4 125 ]]; $ini{performance}{pop2} = [qw[ 192.168.1.2 6 503 10 444 ]]; printdump(\%ini); } { my %ini; $ini{pop1} = [qw[ 192.168.1.1 2 123 2 345 4 125 ]]; $ini{pop2} = [qw[ 192.168.1.2 6 503 10 444 ]]; printdump(\%ini); } __END__
        or even JSON
Re: Ini files
by andreas1234567 (Vicar) on Sep 04, 2009 at 10:07 UTC
    Have you considered Config::IniFiles or one of the other config modules?
    --
    No matter how great and destructive your problems may seem now, remember, you've probably only seen the tip of them. [1]
      He is already using Config::IniFiles. The other config modules are all very similar, syntax varies slightly (ex heredoc support), most don't support duplicates, and they all have various other quirks (and bugs).

      YAML is much better alternative .

      If you have to use ini, I strongly recommend Alias' Config::Tiny http://search.cpan.org/~adamk/Config-Tiny-2.12/lib/Config/Tiny.pm