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

Parsing a config file

by tcf03 (Deacon)
on Mar 10, 2005 at 15:45 UTC ( [id://438303]=perlquestion: print w/replies, xml ) Need Help??

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

I have a script that parses a file that looks like this:
<server> printserver1 default cups cups </server> <server> printserver2 no aix rsh </server>
and Id like it to dump the following:
printserver1,default,cups,cups printserver2,no,aix,rsh
instead it only prints the first line and exits. conceptually I know what Im doing wrong, Im just not sure how to fix it. I need it to iterate over each server as a seperate entity.

Here is the code
#!/usr/bin/perl -w use strict; use diagnostics; #use Data::Dumper; my $config="/var/www/cgi-bin/prt.cfg"; open (CFGFILE,$config) || die "unable to open $config: $!\n"; my ($machine, $default, $type, $connect); my $object=0; my @server; while (my @CFG = <CFGFILE>) { foreach $_(@CFG) { chomp; next if /^#/; next if /^\s/; if (/<server.*?>/i ... /<\/server.*?/i) { next if /<.?server>/i; $object++; push (@server, $_); } ($machine, $default, $type, $connect) = @server; } print "$machine,$default,$type,$connect\n"; }
Thanks in advance for pointing me in the right direction.
Ted

**update** I changed the format of the config file to:
printserver1,default,cups,cups printserver2,no,aix,rsh
and the code to:
#!/usr/bin/perl -w use strict; use diagnostics; #use Data::Dumper; my $config="/var/www/cgi-bin/prt.cfg"; open (CFGFILE,$config) || die "unable to open $config: $!\n"; my ($machine, $default, $type, $connect); my (@server,@arrayref); while (my @X=<CFGFILE>) { foreach $_(@X) { next unless /^\w/i; push (@server, $_); ($machine, $default, $type, $connect)=split(/,/,$_); print "$machine, $default, $type, $connect"; } }
This makes it much easer to deal with. Thanks for the suggestions.

Replies are listed 'Best First'.
Re: Parsing a config file
by dragonchild (Archbishop) on Mar 10, 2005 at 15:58 UTC
    If this is a config file you control, I would change this to use real XML and parse it with XML::Parser or XML::Simple or something like that. Or, even something like Config::ApacheFormat (my personal favorite).

    If you don't control it, I would change how you're doing it.

    use strict; my $config = '/var/www/cgi-bin/prt.cfg'; open( FH, '<', $config ) or die "Cannot open '$config': $!\n"; my @servers; my ($in_server, @temp); while ( <FH> ) { next if /^#|\s/; chomp; if (/<server.*?>/i) { die "Entering server when in server\n" if $in_server; $in_server = 1; } elsif (/<\/server.*?>/) { die "Entering server when in server\n" if !$in_server; $in_server = 0; push @servers, [ @temp ]; @temp = (); } elsif ( $in_server ) { push @temp, $_; } } close FH; use Data::Dumper; print Dumper( \@servers );

    This can be seen as a very simple state machine.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: Parsing a config file
by ikegami (Patriarch) on Mar 10, 2005 at 15:54 UTC
    while (my @CFG = <CFGFILE>) { foreach $_(@CFG) { ... } print ... }

    is the same thing as

    my @CFG = <CFGFILE>; foreach $_(@CFG) { ... } print ...

    since <FILE> returns the entire file as a list of lines when used in a list context.

    Change

    while (my @CFG = <CFGFILE>) { foreach $_(@CFG) { ... } print ... }

    to

    while (<CFGFILE>) { ... print ... }

    to fix the problem.

    Update: You may have other problems; I didn't look deeper. Check out dragonchild's XML suggestion (which I meant to mention myself) and the code he supplied for something better.

Re: Parsing a config file
by Random_Walk (Prior) on Mar 10, 2005 at 16:40 UTC

    Hi there,

    This code is a bit convoluted.

    while (my @X=<CFGFILE>) { foreach $_(@X) { next unless /^\w/i; push (@server, $_); ($machine, $default, $type, $connect)=split(/,/,$_); print "$machine, $default, $type, $connect"; } }
    @X=<CFGFILE> will slurp the file into @X in one go so your while is not looping. I would suggest something like this instead:
    while (<CFGFILE>) { # read each line in turn to $_ next unless /^\w/i; push (@server, $_); # do you realy want this array too ? ($machine, $default, $type, $connect)=split(/,/,$_); print "$machine, $default, $type, $connect"; } }

    Depending on what you do with the data after you have read it you may find something like a hash of arrays or hash of hashes a nice way to store it

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my %servers_HoA; my %servers_HoH; while (<DATA>) { # read each line in turn to $_ next unless /^\w/i; my ($machine, $default, $type, $connect)=split(/,/,$_); print "$machine, $default, $type, $connect"; $servers_HoA{$machine}=[$default, $type, $connect]; # or perhaps $servers_HoH{$machine}{default} = $default; $servers_HoH{$machine}{type} = $type; $servers_HoH{$machine}{connect} = $connect; } print "Hash of Arrays\n"; print Dumper(\%servers_HoA); print "\nHash of Hashes\n"; print Dumper(\%servers_HoH); __DATA__ printserver1,default,cups,cups printserver2,no,aix,rsh
    And here is the output of that last bit of code
    printserver1, default, cups, cups printserver2, no, aix, rsh Hash of Arrays $VAR1 = { 'printserver1' => [ 'default', 'cups', 'cups ' ], 'printserver2' => [ 'no', 'aix', 'rsh ' ] }; Hash of Hashes $VAR1 = { 'printserver1' => { 'type' => 'cups', 'default' => 'default', 'connect' => 'cups ' }, 'printserver2' => { 'type' => 'aix', 'default' => 'no', 'connect' => 'rsh ' } };

    Cheers,
    R.

    Pereant, qui ante nos nostra dixerunt!
      OK moving further along this vein:
      #!/usr/bin/perl -w use strict; use diagnostics; use Data::Dumper; my $config="/var/www/cgi-bin/prt.cfg"; open (CFGFILE,$config) || die "unable to open $config: $!\n"; my (@server,@arrayref); foreach $_(<CFGFILE>) { chomp; next unless /^\w/; @server=split(/,/,$_); push (@arrayref, [ @server ]); } print "servers: $arrayref[0][2]";
      what if I want to call all servers?
      ie print "servers: $arrayref[0 ... $#arrayref][0]
      I know that wont work, but it best explains what I want to do...

        you need to loop through the array holding the refs and de-ref the bits you want to print...

        print "$_->[0]\n" for @arrayrefs # or map may be your friend print "servers: ", (join ", ", map {$_->[0]} @arrayrefs), "\n";
        BTW you can also get rid of the intermediate array @servers by just throwing split straight into an anonymous array
        push @arrayrefs, [split(/,/,$_)];

        Cheers,
        R.

        Pereant, qui ante nos nostra dixerunt!
Re: Parsing a config file
by dragonchild (Archbishop) on Mar 10, 2005 at 19:03 UTC
    (This is in repsonse to your update.)

    If you have a comma-delimited file, I would recommend using Text::xSV to parse it instead of hand-parsing. The reason is that you may want a comma in one of your fields, which means you need to quote the field, but Text::xSV already handles that. Plus, it's clearer from using the module that you mean to have a comma-delimited file.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: Parsing a config file
by thinker (Parson) on Mar 10, 2005 at 16:39 UTC

    Hi tcf03

    if your config file is always that simple, you could use a regex to parse it, like

    #!/usr/bin/perl -l use strict; use warnings FATAL => "all"; my $stuff; { $/= undef; $stuff = <DATA>; } while ($stuff =~ /<server>\n(.*?)<\/server>/msg){ print join ",", split "\n", $1 ; } __DATA__ <server> printserver1 default cups cups </server> <server> printserver2 no aix rsh </server> __END__ this returns printserver1,default,cups,cups printserver2,no,aix,rsh

    If it can get much more complicated than this, you may have to consider alternatives

    I hope this helps

    thinker

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2024-03-19 02:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found