Namuna
Namuna

Reputation: 1030

Perl: command-line override of config file settings?

I'm building a script that utilizes a config file (YAML) to read in all the necessary configuration information, then prints out all the necessary steps a Linux Admin needs to step through to build a server.

A required option is for the Linux Admin that's running the script to be able to override any of the item/value pairs from the config file, at the command-line.

The way I'm currently handling this seems overly cumbersome and I know there's got to be a more innovative and less clunky way to do this.


In the code I:

  1. Parse the YAML config file with YAML::Tiny

    location:
        continent: na
        country: us
        city: rh
    
  2. Create variables with the same names as the config file items, assigning the values from the config file.

    my $yaml = YAML::Tiny->new;
    $yaml = YAML::Tiny->read($config_yml);
    my $continent = $yaml->[0]->{location}->{continent};
    my $country = $yaml->[0]->{location}->{country};
    my $city = $yaml->[0]->{location}->{city};
    
  3. Use Getopt::Long and assign the variables, overriding anything passed at the command-line.

    GetOptions (
        "city=s" => \$city,
        "continent=s" => \$continent,
        "country=s" => \$country,
    );
    

So those are just 3 item/values pairs, my actual config has over 40 and will change...Which makes for a bit of work to have to keep updating. Any suggestions?

Upvotes: 4

Views: 908

Answers (3)

pilcrow
pilcrow

Reputation: 58534

You can let the admin override the YAML settings with a single, flexible switch similar to what ssh(1) does with -o. This is especially appropriate if the config settings are numerous and likely to change.

$ myscript -o location:city=rh --option location:country=us

Now, inside the script, you might keep all your runtime config bundled together in a hash for convenience (rather than having $this_and_that_opt scalars proliferate over time). Option parsing would then look something like this:

# First, set up %GlobalAppCfg from defaults and YAML
# now handle "-o location:country=us"
GetOptions('option|o=s' => sub {
                              my (undef, $optstring) = @_;

                              my ($userkey, $val) = split('=', $optstring, 2);
                              my ($major, $minor) = split(':', $userkey,   2);

                              $GlobalAppCfg->{$major}->{$minor} = $val;
                            },
            ...);

or whatever. You can normalize config keys and values, handle arbitrarily deep key/subkey/subkey configs, etc. This can get slippery, so you might like to key-lock that global hash.

Upvotes: 3

spazm
spazm

Reputation: 4809

Take a look at some of the Config modules that marry GetOpt and YAML, perhaps Config::YAML or Config::YAML::Tiny

Upvotes: 2

drmirror
drmirror

Reputation: 3760

Just a sketch, but

  1. In your GetOptions call, could you use "deep references" into the YAML structure, thereby getting rid of the "intermediate" variables?

  2. By looking at the generated YAML structure, could you not generate the GetOptions call automatically (based on what variables you actually see in the YAML). By generate, I mean, create the call as a string, and then use "eval" to actually execute it.

  3. If you want the "intermediate variables" for convenience, you could probably generate those yourself from the YAML structure, also as a string, and then use "eval" to actually create the variables.

As I said, just a sketch.

Upvotes: 1

Related Questions