Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

scope with multiple files

by fireartist (Chaplain)
on May 22, 2002 at 14:10 UTC ( [id://168425]=perlquestion: print w/replies, xml ) Need Help??

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

I'm wish to use a seperate file to hold my configuration variables in, but I'm having difficulty understanding properly how the scope works under 'strict'.
I've tried every combination that I can think of using 'use', 'use vars', 'my', 'require' and 'do', but I can't get it to run cleanly using 'warnings'. Also, I'd rather understand it properly than blindly trying out out combinations of functions I don't full understand.

I have a file "conf.pl" which contains.
{ var1 => "one", var2 => "two" }
And the main program "test.pl" which contains.
#!/usr/bin/perl -wT use strict; my $conf = do ('G4 HD:Desktop Folder:perl:test:config.pl'); print $conf->{'var1'}, "\n";
However, when I run test.pl I get the following error message
# Use of uninitialized value in print. File 'G4 HD:Desktop Folder:perl:test:test.pl'; Line 6
I've read the docs on 'use' 'do' and 'require', but I still don't understand what i'm doing wrong.
Any pointers would be most appreciated.

Replies are listed 'Best First'.
Re: scope with multiple files
by ariels (Curate) on May 22, 2002 at 15:07 UTC

    Are you sure $conf isn't being set to undef by the do? Read in the manual the paragraphs beginning "If <samp>do</samp> cannot read the file...", to see how to diagnose the precise error occurring.

      ariels you star!

      I changed the line
      my $conf = do ('G4 HD:Desktop Folder:perl:test:config.pl');
      to
      my $conf = do ('G4 HD:Desktop Folder:perl:test:conf.pl');
      And it worked as expected.

      Thank you.
Re: scope with multiple files
by oakbox (Chaplain) on May 22, 2002 at 14:30 UTC
    Well, I couldn't figure out how to make it TAINT safe. Taint complains pretty much any time you try to execute code you are importing from somewhere else (at least, that's my understanding).

    What you should be doing is eval'ing the configuration file. Below, I read in the config.pl file into a scalar, eval it, and pop the results into the $conf hash reference.

    #!/usr/bin/perl -w use strict; my $config_file=(""); # declare this way to prevent warnings open(RE,"config.pl") || die("cannot open config.txt"); while(my $line=<RE>){ $config_file .= $line; } close(RE); my $conf = eval($config_file); # you can look at $@ after #this to see if eval had any errors! print $conf->{'var1'}, "\n"; print $conf->{'var2'}, "\n";
    If you have a LOT of configuration information, you might want to look at something like XML::Simple, which will allow you to create a clean* looking configuration file and use it in your script pretty easily.

    * XML is a two-edged sword. It CAN be clean, but you can make it look ugly if you try :)
    -oakbox

      Yep I think XML is good for this.

      Here is a module I wrote the other day and I use it all the time now (I have a php version also that reads the same config files). Sure it's simple but it gets the job done. Requires XML::LibXML.
      package XML::Config; use strict; use XML::LibXML; use vars qw($VERSION); $VERSION = '0.02'; sub new { my ($proto) = shift; my $class = ref($proto) || $proto; # was it a hash or hashref passed. my $options; if (UNIVERSAL::isa($_[0], 'HASH')) { $options = shift; } else { $options = {@_}; } # check if config file defined and exists. my $file = $options->{file} || die(message => 'no config file defi +ned'); (-r $file) || die('could not find config file'); # parse then get xml data root; my $parser = XML::LibXML->new(); my $doc = $parser->parse_file($file); my $xml_r = $doc->getDocumentElement; # check to see if config file and has types defined. my $types = $xml_r->findvalue('/config/@types'); if (!defined($types)) { die('config file must contain types defini +tion') }; # load all config data into hash reference my $self = {}; foreach my $type (split /,/, $types) { #trim whitespace from each type $type =~ s/^\s*(.*?)\s*$/$1/; foreach my $node ($xml_r->findnodes('//'.$type)) { if (defined($node->findnodes('@name'))){ if (!defined($node->findvalue('text()')) || $node->fin +dvalue('text()') eq ' ' || !defined($node->findvalue('text()'))) { $self->{$type}->{$node->findvalue('@name')} = unde +f; } else { $self->{$type}->{$node->findvalue('@name')} = $nod +e->findvalue('text()'); } } } } # Not needed yet (will need to bless later) # bless($self, $class); return $self; } 1; __END__ =head1 NAME B<XML::Config> - Simple XML to hash reference conversion. But it's han +dy! =head1 SYNOPSIS Example XML config file: <config types="directory, value, message"> <directory name="dir">/xmilo/data</directory> <value name="val">foobar</value> <message name="msg">this is a message</message> </config> Now write some perl: #!/usr/bin/perl -w use strict; use XML::Config; my $config = XML::Config->new( file => '/home/iordy/public_html/xmilo/data/config.xml' ); my $dir = $config->{directory}->{dir}; #$dir == '/xmilo/data'; my $msg = $config->{message}->{msg}; #$msg == 'this is a message +'; my $var = $config->{value}->{val}; #$var == 'foobar'; =head1 DESCRIPTION This module attempts to make loading config files simpler, more unifor +m and language independant (the config file not the module). Sure it's just +an XML to hash reference conversion but it's handy! This module is licensed under the GPL. See the LICENSE section below for more details. =head1 TAGS The config file layout is very simple, there is only one tag that must + be in the config file, ironically it's <config> :) <config types="foo, bar"> <my_config> <!-- some directories --> <foo name="dir">/xmilo/data</foo> <foo name="home">/home/iordy/</foo> <!-- some other directories --> <bar name="lib">/home/iordy/lib</bar> </my_config> </config> =head2 <config> <config> defines a config area of any XML document, this must containt the types="" attribute with a comma delimited list of the types the module will look for. Example: If you plan on using the xml tags: <foo name="myfoo">this is my foo</foo> <bar name="mybar">this is my bar</bar> Then your config tag will look like: <config types="foo, bar">config content</config> =head1 METHODS =head2 new() Call new with a filename and it will return a hash of config value +s: my $config = XML::Config->new( file => '/home/iordy/config.xml' ); =head2 get() At the moment there is not get() method because it's simpler to ju +st return a hash reference. If for some reason it becomes worthwhile +it's easy to add later. Example: my $example = $config->{type}->{name}; =head1 WEBSITE You can find out more info here: http://www.iordy.com =head1 AUTHOR Shane Hanna (iordy@iordy.com) =head1 LICENSE XML::Config; Store your stuff in XML. Copyright (C) 2002 Shane Hanna (iordy@iordy.com) This module is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later v +ersion. =cut
Re: scope with multiple files
by fireartist (Chaplain) on May 23, 2002 at 09:00 UTC
    I've now included a method to check that the file is opened successfully, and so prevent silly typos like above.
    #!/usr/bin/perl -wT use strict; my $conf; unless ($conf = do ('G4 HD:Desktop Folder:perl:test:conf.pl')) { die ("Could not open file"); } print $conf->{'var1'}, "\n";

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (2)
As of 2024-04-26 06:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found