Reputation: 163
I want to process script options with these rules:
Without requirement 4, I can just process the config file first, then the command line, allowing the later options to overwrite the earlier. Elegant.
But if the config file is specified on the command line, I need to throw it all away and start again, which makes everything suddenly messy and complicated.
Is there an elegant perl idiom for this?
Upvotes: 1
Views: 257
Reputation: 2808
Rule 5 complicates things... However, modules loaded via -M on the command line are loaded before modules mentioned in your script - including Getopt::Long. Therefore, we can rig something up where the default config is stored in a short, custom module;
# DefaultConf.pm
use strict;
use warnings;
while (<DATA>) {
chomp ; # Remove the newline
s/ \s* \#.* //x ; # Strip comments
next if / ^ \s* $ /x ; # Ignore blank lines
my @a = split(" ", $_, 2); # Break into 2 pieces and unshift
unshift(@ARGV, $_) for reverse @a ; # onto ARGV in reverse order
}
# print "===\n", join(" ", @ARGV), "\n", "===\n"; # debug
1;
__DATA__
# Default App config data here
--verbose
-n # single old-style switch with comment
# another comment
--files file1.txt, file2.txt
Text after __DATA__
is ignored by perl but is available to the programmer via the DATA
filehandle. Here we can store our default config for the script with one restriction - only one option (with arguments) per line. Comments can be used either on the end of an option specification or for a whole line using the perl comment character, '#'.
This is used from the command line like so:
perl -MDefaultConf my_app.pl --verbose=0 --files file12.txt --log
my_app.pl
loads Getopt::Long
as normal but as our custom module is loaded via -M on the command line, it is loaded first. As a result, we can fiddle with @ARGV
before Getopt::Long
gets its greedy little hands on it. Thereafter, the actual option processing is done by Getopt::Long
, so the option syntax at the end of our custom module is exactly the same.
Hopefully the inline comments make the code self explanatory - the only trickery is when there are two "pieces" (say, an option and its single argument), we need to push them onto the front of @ARGV (unshift - not push) in reverse order so that if the same option is mentioned on the command line, it is processed latter and therefore takes precedence.
Using this method you could have numerous config files for different "run situations" selected via the -M option or, as requested, you can run without any of them using CLI for options, or a combination where the CLI options take precedence.
One final comment about rule 5 - only core modules. Because of this restriction, you have to use this (or similar) module - which is NOT a core module. And although its small and easy to understand, it has no tests and it hasn't been deployed in hundreds of places as say, Config::Tiny
has. Config::Tiny
is under 100 lines long (excluding blanks and comments) and can be fully understood in about an hour. Of course, everyone's environment and constraints are different - it's just something to consider.
Upvotes: 1