App::Options(3pm)
πΈ NAME πΈ
App::Options β the module that combines command line options, environment vars, and option file values so your program can just know its configuration π
β¨
β¨ SYNOPSIS β¨
Okay bestie, here's the vibe:
#!/usr/bin/perl -w
use strict;
use App::Options; # reads option values into %App::options by default
# do something with the options (in %App::options)
use DBI;
$dsn = "dbi:mysql:database=$App::options{dbname}";
$dbh = DBI->connect($dsn, $App::options{dbuser}, $App::options{dbpass});
...
Get help from the command line (assuming the program is named prog) ...
prog -?
prog --help
Option values may be provided on the command line, in environment variables, and in option files. (i.e. $ENV{APP_DBNAME} would set the value of %App::options{dbname} by default.)
The dbname and other options could also be set in one of the following configuration files:
/etc/app/policy.conf
$HOME/.app/prog.conf
$HOME/.app/app.conf
$PROGDIR/prog.conf
$PROGDIR/app.conf
$PREFIX/etc/app/prog.conf
$PREFIX/etc/app/app.conf
/etc/app/app.conf
with a file format like
[prog]
dbname = prod
dbuser = scott
dbpass = tiger
See below for a more detailed explanation of these and other advanced features. π«Ά
π DESCRIPTION π
App::Options combines command-line arguments, environment variables, option files, and program defaults to produce a hash of option values. That's it, that's the whole glow-up. β¨
π¦ RELATION TO OTHER CONFIGURATION/OPTION PARSING MODULES π¦
A number of modules are posted on CPAN which do command-line processing.
http://search.cpan.org/modlist/Option_Parameter_Config_Processing
App::Options is different than most of the Getopt::* modules because it integrates the processing of command line options, environment variables, and config files. no cap. π
Furthermore, its special treatment of the perlinc option facilitates the inclusion (use) of application-specific perl modules from special places to enable the installation of multiple versions of an application on the same system (i.e. /usr/myproduct/version).
The description of the AppConfig distribution sounds similar to what is described here. However, the following are some key differences. π
App::Optionsdoes its option processing in theBEGINblock. This allows for the@INCvariable to be modified in time for subsequentuseandrequirestatements.App::Options"sections" (i.e.[cleanup]) are conditional. It is conditional inApp::Options, allowing you to use one set of option files to configure an entire suite of programs and scripts. In AppConfig, the section name is simply a prefix which gets prepended to subsequest option names.App::Optionsconsults a cascading set of option files. These files include those which are system global, project global, and user private. This allows for system administrators, project developers, and individual users to all have complementary roles in defining the configuration values.App::Optionsis not a toolkit but a standardized way of doing option processing. With AppConfig, you still have to decide where to put config files, and you still have to code the--helpfeature. WithApp::Options, you simplyuse App::Options;and all the hard work is done. Advanced options can be added later as necessary as args to theuse App::Options;statement.
App::Options is also the easiest command-line processing system that I have found anywhere. It then provides a smooth transition to more advanced features only as they are needed. Every single quick and dirty script I ever write from now on can afford to use App::Options. π
The documentation of App::Options takes three forms below. π
- API Reference β describing the API (methods, args)
- Logic Flow β describing the order and logic of processing
- Usage Tutorial β describing how to use the API in practical situations
π RELATION TO THE P5EE PROJECT π
App::Options was motivated by and supports the P5EE/App-Context variant of the Perl 5 Enterprise Environment (P5EE). However, App::Options has no dependency on any other module in the P5EE project, and it is very useful without any knowledge or use of other elements of the P5EE project. fr fr. π
See the P5EE web sites for more information on the P5EE project.
http://www.officevision.com/pub/p5ee/index.html
π API REFERENCE: Methods π
init() π
- Signature:
App::Options->init(); - Signature:
App::Options->init(%named); - Signature:
App::Options->init($myvalues); - Signature:
App::Options->init($myvalues, %named);
(NOTE: %named represents a list of name/value pairs used as named args. Params listed below without a $ are named args.)
- Param:
$myvaluesHASH β specify a hash reference other than%App::optionsto put configuration values in. - Param:
valuesHASH β specify a hash reference other than%App::optionsto put configuration values in. - Param:
optionsARRAY β specify a limited, ordered list of options to be displayed when the--helpor-?options are invoked - Param:
optionHASH β specify additional attributes of any of the various options to the program (see below) - Param:
no_cmd_argsβ do not process command line arguments - Param:
no_env_varsβ do not read environment variables - Param:
no_option_fileβ do not read in the option file(s) - Param:
print_usageβ provide an alternateprint_usage()function - Return:
void - Throws:
"App::Options->init(): must have an even number of vars/values for named args" - Throws:
"App::Options->init(): 'values' arg must be a hash reference" - Throws:
"App::Options->init(): 'option' arg must be a hash reference" - Throws:
"App::Options->init(): 'options' arg must be an array reference" - Since:
0.60
Sample Usage: (normal) β¨
use App::Options; # invokes init() automatically via import()
okay so that's literally it, bestie. This is functionally equivalent to the following, but that's not near as nice to write at the top of your programs. π
BEGIN {
use App::Options qw(:none); # import() does not call init()
App::Options->init(); # we call init() manually
}
Or we could have used a more full-featured version ... the deluxe edition, the one with ALL the toppings π
use App::Options (
values => \%MyPackage::options,
options => [ "option_file", "prefix", "app",
"perlinc", "debug_options", "import", ],
option => {
option_file => { default => "~/.app/app.conf" }, # set default
app => { default => "app", type => "string" }, # default & type
prefix => { type => "string", required => 1; env => "PREFIX" },
perlinc => undef, # no default
debug_options => { type => "int" },
import => { type => "string" },
flush_imports => 1,
},
no_cmd_args => 1,
no_env_vars => 1,
no_option_file => 1,
print_usage => sub { my ($values, $init_args) = @_; print "Use it right!\n"; },
);
The init() method is usually called during the import() operation when the normal usage (use App::Options;) is invoked.
The init() method reads the command line args (@ARGV), then finds an options file, and loads it, all in a way which can be done in a BEGIN block (minimal dependencies). This is important to be able to modify the @INC array so that normal use and require statements will work with the configured @INC path. iykyk β this is the whole reason it's built to run that early. π
The following named arguments are understood by the init() method.
valuesβ specify a hash reference other than%App::optionsto put option values in.optionsβ specify a limited, ordered list of options to be displayed when the--helpor-?options are invokedoptionβ specify optional additional information about any of the various options to the program (see below)no_cmd_argsβ do not process command line argumentsno_env_varsβ do not read environment variablesno_option_fileβ do not read in the option fileshow_allβ force showing all options in--helpeven whenoptionslist specifiedprint_usageβ provide an alternateprint_usage()functionargs_descriptionβ provide descriptive text for what the args of the program are (command line args after the options). This is printed in the usage page (--helpor-?). By default, it is simply"[args]".
The additional information that can be specified about any individual option variable using the option arg above is as follows. this is the good stuff, the β¨customizationβ¨ part π¦
defaultβ the default value if none supplied on the command line, in an environment variable, or in an option filerequiredβ the program will not run unless a value is provided for this optiontypeβ if a value is provided, the program will not run unless the value matches the type ("string","integer","float","boolean","date","time","datetime",/regexp/).envβ a list of semicolon-separated environment variable names to be used to find the value instead of"APP_{VARNAME}".descriptionβ printed next to the option in the"usage"pagesecureβ identifies an option as being"secure"(i.e. a password) and that it should never be printed in plain text in a help message (-?). All options which end in"pass","passwd", or"password"are also assumed to be secure unless asecure => 0setting exists. If the value of the"secure"attribute is greater than 1, a heightened security level is enforced: 2=ensure that the value can never be supplied on a command line or from the environment but only from a file that only the user running the program has read/write access to. This value will also never be read from the environment or the command line because these are visible to other users. If thesecurity_policy_levelvariable is set, any true value for the"secure"attribute will result in the value being set to the"security_policy_level"value.value_descriptionβ printed within angle brackets ("<>") in the"usage"page as the description of the option value (i.e.--option_name=<value_description>)
β οΈ real talk about that secure one: when it kicks in at level 2, that value is NOT coming from the command line or the environment, periodt. Only from a file locked down to the user running the program. Do not fight this, it is protecting you. π
The init() method stores command line options and option file values all in the global %App::options hash (unless the "values" argument specifies another reference to a hash to use).
The special options are as follows. these ones are the main characters π
option_fileβ specifies the exact file name of the option file to be used (i.e."app --option_file=/path/to/app.conf").appβ specifies the tag that will be used when searching for an option file. (i.e."app --app=myapp"will search for"myapp.conf"before it searches for"app.conf")"app"is automatically set with the stem of the program file that was run (or the first part ofPATH_INFO) if it is not supplied at the outset as an argument.prefixβ This represents the base directory of the software installation (i.e."/usr/myproduct/1.3.12"). If it is not set explicitly, it is detected from the following places:PREFIXenvironment variable- the real path of the program with
/binor/cgi-binstripped /usr/local(or whatever"prefix"perl was compiled with)
If it is autodetected from one of those three places, that is only provisional, in order to find the "option_file". The "prefix" variable should be set authoritatively in the "option_file" if it is desired to be in the $values structure. so like, the autodetect is a lil scout, not the final boss. okay? π§
perlincβ a path of directories to prepend to the@INCsearch path. This list of directories is separated by any combination of[,; ]characters.debug_optionsβ if this is set, a variety of debug information is printed out during the option processing. This helps in debugging which option files are being used and what the resulting variable values are. The following numeric values are defined. π1= print the basic steps of option processing2= print each option file searched, final values, and resulting@INC3= print each value as it is set in the option hash4= print overrides fromENVand variable substitutions5= print each line of each file withexclude_sectionindicator6= print option file section tags, condition evaluation, and each value found (even if it is not set in the final values)7= print final values
importβ a list of additional option files to be processed. An imported file goes on the head of the queue of files to be processed.hostnameβ the hostname as returned by thehostname()function provided bySys::Hostname(may or may not include domain qualifiers as a fully qualified domain name).hostβ same ashostname, but with any trailing domain name removed. (everything after the first".")flush_importsβ flush all pending imported option files.security_policy_levelβ When set, this enforces that whenever secure attributes are applied, they are set to the same level. When set0, all of the security features are disabled (passwords can be viewed with"--security_policy_level=0 --help"). When set to2, all secure options can only be read from files which do not have read/write permission by any other user except the one running the program.
π genuinely, --security_policy_level=0 will show you the passwords in the help output. That is a real thing that really happens. Do not do this on a shared box, bestie.
π LOGIC FLOW: OPTION PROCESSING DETAILS π
Basic Concept β By calling App::Options->init(), your program parses the command line, environment variables, and option files, and puts var/value pairs into a global option hash, %App::options. Just include the following at the top of your program in order to imbue it with many valuable option-setting capabilities. β¨
use App::Options;
When you "use" the App::Options module, the import() method is called automatically. This calls the init() method, passing along all of its parameters.
One of the args to init() is the "values" arg, which allows for a different hash to be specified as the target of all option variables and values.
use App::Options (values => \%Mymodule::opts);
Throughout the following description of option processing, the %App::options hash may be referred to as the "options hash". However it will be understood that some other hash (as specified by the "values" arg) may actually be used. just mentally swap it in whenever you see it, mmkay? π
π Command Line Arguments π
Unless the "no_cmd_args" arg is specified to init(), the first source of option values is the command line.
Each command line argument that begins with a "-" or a "--" is considered to be an option. It may take any form such as
so these are literally all the shapes an option can show up in, bestie:
--verbose # long option, no arg
--verbose=5 # long option, with arg
--city=ATL # long option, with arg
-x # short option, no arg
-t=12:30 # short option, with arg
Every option it sniffs out gets shifted out of @ARGV, and the values land in the options hash (%App::options). Options with no arg? They're understood to have a value of "1". So --verbose is literally identical to --verbose=1. no cap. β¨
And naturally β the -- option terminates command line option processing. periodt. π
π¦ Command Line Argument Variable Substitution
Any value that includes a variable goes through variable substitution before it gets placed in the option hash. i.e.
logdir = ${prefix}/log
This line will be expanded properly. (Of course, the variable and its value should be already set in the option hash. obviously.)
Variable substitution is also performed to interpolate values from the environment:
port = $ENV{HTTP_PORT}
πΈ Special Option "app"
If the special option, app, was not given on the command line, it gets initialized. This option is useful for including or excluding different sections of the option files.
To handle the special case that the program is running in a CGI environment, the PATH_INFO variable is checked first. The first segment of the PATH_INFO is stripped off, and that becomes the value of the app option.
Otherwise, the stem of the program name becomes the value of the app option. The stem is the program name without any trailing extension (i.e. ".exe", ".pl", etc.).
π The Program Directory
One of the places that will be searched for option files is the directory in which the program exists on the file system. This directory is known internally as $prog_dir.
π Special Option "prefix"
The special option, prefix, represents the root directory of the software installation. On a Unix system, a suite of software might by installed at /usr/myproduct/thisversion, and that would be the prefix. Under this directory, you would expect to find the src, bin, lib, and etc directories, as well as perhaps cgi-bin, htdocs, and others.
If the prefix option is not specified on the command line, the $PREFIX environment variable is used.
If that is not set, the $prog_dir with the trailing /bin or /cgi-bin stripped off is used.
π Option Files
Unless the no_option_file arg is specified to init(), the next source of option values is the option files.
By default, a cascading set of option files are all consulted to allow individual users to specify values that override the normal values for certain programs. Furthermore, the values for individual programs can override the values configured generally system-wide.
The resulting value for an option variable comes from the first place that it is ever seen. Subsequent mentions of the option variable within the same or other option files will be ignored.
The following files are consulted in order.
$ENV{HOME}/.app/$app.conf
$ENV{HOME}/.app/app.conf
$prog_dir/$app.conf
$prog_dir/app.conf
$prefix/etc/app/$app.conf
$prefix/etc/app/app.conf
/etc/app/app.conf
Thus, a system administrator might set up the $prefix/etc/app/app.conf file with system-wide defaults. All option configuration could be done in this single file, separating the relevant variables into different sections for each different program to be configured.
However, if the administrator decided that there were too many parameters for a single program such that it cluttered this file, he might put the option values for that program into the $prefix/etc/app/$app.conf file. This distinction is a matter of preference, as both methods are equally functional.
A program developer may decide to override some of the system-wide option values for everyone by putting option files in the program's own directory.
Furthermore, a user may decide to override some of the resulting option values by putting some option files in the appropriate place under his home directory.
This separation of config files also allows for secure information (such as database passwords) to be required to be provided in the user's own (secured) option files rather than in read-only system-wide option files.
Specifying the --debug_options option on the command line will assist in figuring out which files App::Options is looking at. π
π§ Option File Format
In general an option file takes the form of lines with var = value.
dbname = prod # this is the production database
dbuser = scott
dbpass = tiger
Trailing comments (preceded by a "#") are trimmed off. Spaces before and after the variable, and before and after the value are all trimmed off. Then enclosing double-quotes (") are trimmed off. Variables can be any of the characters in [a-zA-Z0-9_.-]. Values can be any printable characters or the empty string. Any lines which aren't recognizable as var = value lines or section headers (see below) are ignored.
If certain variables should be set only for certain programs (or under certain other conditions), section headers may be introduced. The special section headers "[ALL]" and "[]" specify the end of a conditional section and the resumption of unconditional option variables.
[progtest]
dbname = test # this is the test database
[ALL]
dbname = prod # this is the production database
dbuser = scott
dbpass = tiger
In this case, the "progtest" program will get "dbname = test" while all other programs will get "dbname = prod".
Note that you would not get the desired results if the "dbname = prod" statement was above the "[progtest]" header. Once an option variable is set, no other occurrence of that variable in any option file will override it. π
For the special case where you want to specify a section for only one variable as above, the following shortcut is provided.
[progtest] dbname = test # this is the test database
dbname = prod # this is the production database
dbuser = scott
dbpass = tiger
The "[progtest]" section header applied for only the single line.
Furthermore, if it were desired to make this override for all programs containing "test" in them, you would use the following syntax.
[/test/] dbname = test # this is the test database
dbname = prod # this is the production database
dbuser = scott
dbpass = tiger
The "[/test/]" section header tested the "app" option using an arbitrary regular expression.
The section headers can create a condition for inclusion based on any of the variables currently in the option hash. In fact, "[progtest]" is just a synonym for "[app=progtest]" and "[/test/]" is a synonym for "[app=/test/]".
If, for instance, the usernames and passwords were different for the different databases, you might have the following.
[/test/] dbname = test # progs with "test" go to test database
dbname = prod # other progs go to the production database
[dbname=test] # progs
dbuser = scott
dbpass = tiger
[dbname=prod]
dbuser = mike
dbpass = leopard
The conditions created by a section header may be the result of more than a single condition.
[dbname=test;dbuser=scott]
dbpass = tiger
[dbname=test;dbuser=ken]
dbpass = ocelot
[dbname=prod;dbuser=scott]
dbpass = tiger62
[dbname=prod;dbuser=ken]
dbpass = 3.ocelot_
Any number of conditions can be included with semicolons separating them.
Each time a variable/value pair is found in an option file, it is only included in the option hash if that variable is currently not defined in the option hash. Therefore, option files never override command line parameters. π
π Option Environment Variables and Variable Substitution
For each variable/value pair that is to be inserted into the option hash from the option files, the corresponding environment variables are searched to see if they are defined. The environment always overrides an option file value. (If the no_env_vars arg was given to the init() method, this whole step of checking the environment is skipped.)
By default, the environment variable for an option variable named "dbuser" would be "APP_DBUSER". However, if the "env" attribute of the "dbuser" option is set, a different environment variable may be checked instead (see the Tutorial below for examples).
After checking the environment for override values, any value which includes a variable undergoes variable substitution before it is placed in the option hash.
π Setting Environment Variables from Option Files
Any variable of the form "ENV{XYZ}" will set the variable XYZ in the environment rather than in the options hash. Thus, the syntax
ENV{LD_LIBRARY_PATH} = ${prefix}/lib
will enhance the LD_LIBRARY_PATH appropriately.
Note that this only works for options set in an options file. It does not work for options set on the command line, from the environment itself, or from the program-supplied default. π
Under some circumstances, the perl interpreter will need to be restarted in order to pick up the new LD_LIBRARY_PATH. In that case, you can include the special option
perl_restart = 1
An example of where this might be useful is for CGI scripts that use the DBI and DBD::Oracle because the Oracle libraries are dynamically linked at runtime.
NOTE: The other standard way to handle CGI scripts which require special environment variables to be set is with Apache directives in thehttpd.confor.htaccessfiles. i.e.
SetEnv LD_LIBRARY_PATH /home/oracle/oracle/product/10.2.0/oraclient/lib
SetEnv ORACLE_HOME /home/oracle/oracle/product/10.2.0/oraclient
NOTE: Yet another standard way to handle CGI scripts which require an enhancedLD_LIBRARY_PATHspecifically is to use the/etc/ld.so.conffile. Edit/etc/ld.so.confand then runldconfig(as root). This adds your specific path to the"standard system places"that are searched for shared libraries. This has nothing to do with App::Options or environment variables of course. π«Ά
β import and flush_imports
After each option file is read, the special option "flush_imports" is checked. If set, the list of pending option files to be parsed is cleared, and the flush_imports option is also cleared.
This is useful if you do not want to inherit any of the option values defined in system-wide option files. π§
The special option "import" is checked next. If it is set, it is understood to be a list of option files (separated by /[,; ]+/) to be prepended to the list of pending option files. The import option itself is cleared.
πΈ Other Environment Variables and Defaults
okay so after the command line options and the option files have been parsed, all the other options the program knows about get checked for environment variables and defaults. like, everybody gets a turn π
Options can be defined for the program with either the "options" arg or the "option" arg to the init() method (or a combo of both, we're flexible like that):
use App::Options (
options => [ "dbname", "dbuser", "dbpass" ],
option => {
dbname => {
env => "DBNAME",
default => "devel",
},
dbuser => {
env => "DBUSER;DBI_USER",
},
dbpass => {
env => "", # password in %ENV is security breach
},
},
);
So for every option variable that's known, if the value isn't already set, we check the environment, then we check the default, then we do variable expansion, and finally the value gets dropped into the option hash. that's the whole pipeline bestie β¨
π Special Option prefix
The special option "prefix" gets reconciled and finalized next.
Unless it was specified on the command line, the original "prefix" was autodetected. and ngl, that might have given you a path that was technically correct but not the one you meant, because of symbolic linking on the file system. like, it's not wrong, it's just... not what you wanted π
And since the "prefix" variable can also be set in an option file, there might be a difference between the auto-detected "prefix" and the option file "prefix". If that happens? The option file "prefix" is the one that wins. it's the authority. it's the main character. π
π¦ Special Option perlinc
One of the primary design goals of App::Options was to be able to support multiple installations of software on a single machine. obsessed.
So like, you might have different versions of software installed under various directories such as
/usr/product1/1.0.0
/usr/product1/1.1.0
/usr/product1/2.1.5
Naturally, slightly different versions of your perl modules will be installed under each different "prefix" directory. So when a program runs from /usr/product1/1.1.0/bin, the "prefix" will be /usr/product1/1.1.0, and we want the @INC variable to be modified so that the appropriate perl modules get included from $prefix/lib/*.
And this is where the "perlinc" option comes in. π«Ά
- If
"perlinc"is set, it's understood to be a list of paths (separated by/[ ,;]+/) to be prepended to the@INCvariable. - If
"perlinc"is not set,$prefix/lib/perl5/$perlversionand$prefix/lib/perl5/site_perl/$perlversionare automatically prepended to the@INCvariable as a best guess.
π Special Option debug_options
If the "debug_options" variable is set (often on the command line), three things get printed out:
- the list of option files that was searched
- the resulting list of variable values
- the resulting list of include directories (
@INC)
it's giving β¨full transparencyβ¨ and honestly? we love a debugging moment.
β Version
After all the values have been parsed, various conditions get checked to see if the program should print diagnostic information instead of just, y'know, continuing to run. Two of these examples are --version and --help.
If the "--version" option is set on the command line, the version information for all loaded modules is printed, and the program is exited. (The version of a package/module is assumed to be the value of the $VERSION variable in that package. i.e. The version of the XYZ::Foo package is $XYZ::Foo::VERSION.)
prog --version
Of course, this is all done implicitly in the BEGIN block (during use App::Options;). So if your program tried to set $main::VERSION, it may not be set unless it's set explicitly in the BEGIN block. sneaky little timing thing, iykyk π
#!/usr/bin/perl
BEGIN {
$VERSION = "1.12";
}
use App::Options;
This can be integrated with CVS file versioning using something like the following:
#!/usr/bin/perl
BEGIN {
$VERSION = do { my @r=(q$Revision: 14478 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r};
}
use App::Options;
Furthermore, the version information about some modules that you might expect to have seen will not be printed, because those modules have not yet been loaded. To fix this, use the --version_packages option (or set it in an option file). This option contains a comma-separated list of modules and/or module regular expressions. The modules are loaded, and the version information from all resulting packages that match any of the patterns is printed.
prog --version --version_packages=CGI
prog --version --version_packages=CGI,Template
This also cuts down on the miscellaneous modules (and pragmas) which might have cluttered up your view of the version information you were actually interested in. And if you really wish to see version information for all modules, use the --version=all option.
prog --version=all --version_packages=CGI,Template
π«Ά Help and Validations
If the "-?" or "--help" options were set on the command line, the usage statement is printed, and the program is exited. bye bestie, hope you found what you needed π
Then each of the options which is defined may be validated.
If an option is designated as "required", its value must be defined somewhere (although it may be the empty string). (If it's also required to be a non-empty string, a regex may be provided for the type, i.e. type => "/./".)
If an option is designated as having a "type", its value must either be undefined or match a specific regular expression.
stringβ(any)integerβ/^-?[0-9_]+$/floatβ/^-?[0-9_]+\.?[0-9_]*([eE][+-]?[0-9_]+)?$/float(the other flavor) β/^-?\.[0-9_]+([eE][+-]?[0-9_]+)?$/booleanβ/^[01]$/dateβ/^[0-9]{4}-[01][0-9]-[0-3][0-9]$/datetimeβ/^[0-9]{4}-[01][0-9]-[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]$/timeβ/^[0-2][0-9]:[0-5][0-9]:[0-5][0-9]$//regexp/β/regexp/
Note that an arbitrary regular expression may be designated as the "type" by enclosing it in slashes (i.e. "/^[YN]$/"). cute, right?
And if the options fail any of the "required" or "type" validation tests, the App::Options::print_usage() function is called to print out a usage statement and the program is exited. no passing go, no collecting $200.
π USAGE TUTORIAL
π Getting Started
Create a perl program called "demo1".
#!/usr/bin/perl
use App::Options;
print "Wow. Here are the options...\n";
foreach (sort keys %App::options) { # options appear here!
printf("%-20s => %s\n", $_, $App::options{$_});
}
Run it different kinds of ways to see how it responds. go feral, try them all π
demo1
demo1 -x
demo1 -x --verbose
demo1 --x -verbose
demo1 -x=5 --verbose=10 --foo=bar
demo1 --help
demo1 -x=8 --help
demo1 -?
demo1 --debug_options -?
demo1 -x=5 --verbose=10 --foo=bar --debug_options -?
demo1 --version
demo1 --version --version_packages=CGI
Now create a copy of the program.
cp demo1 demo2
Start putting entries like the following
x = 7
hello = world
[demo2]
verbose=3
[/demo/]
baz = foo
in the following files
$HOME/.app/demo1.conf
$HOME/.app/demo2.conf
$HOME/.app/app.conf
demo1.conf (same directory as the demo* programs)
demo2.conf (same directory as the demo* programs)
app.conf (same directory as the demo* programs)
$PREFIX/etc/app/demo1.conf
$PREFIX/etc/app/demo2.conf
$PREFIX/etc/app/app.conf
/etc/app/app.conf
and see how the programs respond in each different case. science time π§
Next, set environment variables like the following and see how the programs respond.
export APP_X=14
export APP_VERBOSE=7
export APP_FOO=xyzzy
export APP_HELLO=Plugh!
You are well on your way. β¨
π₯ A Development Scenario
Now let's imagine that we're writing a suite of programs which operate on a relational database. These programs are part of a larger system which goes through a development cycle of development, test, and production. Each step in the development cycle, the programs will run against different databases, but we don't want that to affect the code.
Let's suppose that we write a program which lists the customers in a customer table.
create table person (
person_id integer not null auto_increment primary key,
first_name varchar(99) null,
last_name varchar(99) null,
birth_dt date null,
company_id integer null,
wholesale_ind char(1) null,
change_dttm datetime not null,
);
We call this program "listcust".
#!/usr/bin/perl -e
use strict;
use App::Options;
use DBI;
my $dsn = "dbi:$App::options{dbdriver}:database=$App::options{dbname}";
my $dbh = DBI->connect($dsn, $App::options{dbuser}, $App::options{dbpass});
my $sql = "select first_name, last_name, birth_dt, company_id, wholesale_ind, change_dttm from person";
my $cust = $dbh->selectall_arrayref($sql);
foreach my $row (@$cust) {
printf("%-24 %-24 %s %9d %s\n", @$row);
}
$dbh->disconnect();
Then you can invoke this program with all of the command line options and everything works fine. slay π
listcust --dbdriver=mysql --dbname=prod --dbuser=scott --dbpass=tiger
However, if you don't use all of the options, you will get a DBI error. And furthermore, "listcust --help" doesn't help very much. π A system administrator confronting this problem would put the following lines into "$PREFIX/etc/app/app.conf" or "$PREFIX/etc/app/listcust.conf".
dbdriver = mysql
dbname = prod
dbuser = scott
dbpass = tiger
If, however, your projects were not in the habit of using the PREFIX environment variable and the program is not installed in $PREFIX/bin, he would have to put the above lines in either the "app.conf" file or the "listcust.conf" file in the same directory as "listcust" or in the global "/etc/app/app.conf" option file.
A user (without privileges to the "$PREFIX/etc/app" directory or the directory in which "listcust" lives) would have to put the described lines into "$HOME/.app/app.conf" or "$HOME/.app/listcust.conf".
Putting the options in any of those files would make "--help" print something intelligent. character development!! π
A developer, however, might decide that the program should have some defaults.
use App::Options (
option => {
dbdriver => "mysql",
dbname => "prod",
dbuser => "scott",
dbpass => "tiger",
},
);
(This supplies defaults and also makes --help print something intelligent, regardless of whether there are any configuration files.)
So if all you ever wanted was defaults for your options? bestie, that format is literally fine. It works. But "default" is not the only cute attribute an option can have β there's a whole wardrobe of them. The moment you want any of those, you reach for the fuller, main-character form of the option arg:
use App::Options (
option => {
dbdriver => { default => "mysql", },
dbname => { default => "prod", },
dbuser => { default => "scott", },
dbpass => { default => "tiger", },
},
);
Now let's say those options are all required. If they don't show up, the program does not run, periodt.
Meanwhile, giving a password a default is... not it. We can drop the default β but heads up, if you ever tried running the program without handing over the password, it would not even get past printing a usage statement. π«
use App::Options (
option => {
dbdriver => { required => 1, default => "mysql", },
dbname => { required => 1, default => "prod", },
dbuser => { required => 1, default => "scott", },
dbpass => { required => 1, },
},
);
Now maybe we wanna level up the code so it lists only the customers with certain attributes. It's giving very selective β¨
my $sql = "select first_name, last_name, birth_dt, company_id, wholesale_ind, change_dttm from person";
my (@where);
push(@where, "first_name like '%$App::options{first_name}%'")
if ($App::options{first_name});
push(@where, "last_name like '%$App::options{last_name}%'")
if ($App::options{last_name});
push(@where, "birth_dt = '$App::options{birth_dt}'")
if ($App::options{birth_dt});
push(@where, "company_id = $App::options{company_id}")
if ($App::options{company_id});
push(@where, "wholesale_ind = '$App::options{wholesale_ind}'")
if ($App::options{wholesale_ind});
push(@where, "change_dttm >= '$App::options{change_dttm}'")
if ($App::options{change_dttm});
if ($#where > -1) {
$sql .= "\nwhere " . join("\n and ", @where) . "\n";
}
my $cust = $dbh->selectall_arrayref($sql);
And the init() call could get glowed up to look like this. Also β icon behaviour unlocked β the order your options get printed by --help can be set with the options argument. (Otherwise they'd print in alphabetical order, which is fine but lowkey basic.)
use App::Options (
options => [ "dbdriver", "dbname", "dbuser", "dbpass",
"first_name", "last_name", "birth_dt", "company_id",
"wholesale_ind", "change_dttm",
],
option => {
dbdriver => {
description => "dbi driver name",
default => "mysql",
env => "DBDRIVER", # use a different env variable
required => 1,
},
dbname => {
description => "database name",
default => "prod",
env => "DBNAME", # use a different env variable
required => 1,
},
dbuser => {
description => "database user",
default => "scott",
env => "DBUSER;DBI_USER", # check both
required => 1,
},
dbpass => {
description => "database password",
env => "", # disable env for password (insecure)
required => 1,
secure => 1, # FYI. This is inferred by the fact that "dbpass"
# ends in "pass", so it is not necessary.
},
first_name => {
description => "portion of customer's first name",
},
last_name => {
description => "portion of customer's last name",
},
birth_dt => {
description => "customer's birth date",
type => "date",
},
company_id => {
description => "customer's company ID",
type => "integer",
},
wholesale_ind => {
description => "indicator of wholesale customer",
type => "/^[YN]$/",
},
change_dttm => {
description => "changed-since date/time",
type => "datetime",
},
},
);
Okay, note this in the example above: the default environment variable name (APP_${varname}) has been overridden for some of these options. dbname gets set from DBNAME instead of APP_DBNAME. And dbuser gets set from either DBUSER or DBI_USER.
Also also β if all you wanted was the order of the options, and not every single attribute, you could've just done this:
use App::Options (
options => [ "dbdriver", "dbname", "dbuser", "dbpass",
"first_name", "last_name", "birth_dt", "company_id",
"wholesale_ind", "change_dttm",
],
);
Using the options arg makes the options print in the order you gave in the --help output. Then the remaining options defined in the option arg print in alphabetical order. Every other option that's set on the command line or in option files gets printed if the show_all option is set. And show_all is off by default if either the options arg or the option arg is supplied, and on if neither of them is. π
Now, if for some reason your program needed to dump the options into a different option hash (instead of %App::options), or straight-up name the option file to use (disregarding the standard option file search path), it can do that with this syntax:
use App::Options (
values => \%Mymodule::opts,
option_file => "/path/to/options.conf",
);
And if for some reason the program needs to inhibit one or more of the sources for options, one of these arguments does the trick. Obviously, inhibiting all three would be a bit silly. π
use App::Options (
no_cmd_args => 1,
no_option_file => 1,
no_env_vars => 1,
);
π A Deployment Scenario π
Sometimes a whole software system gets deployed across so many machines, and you want a single option file to set different values depending on which machine it landed on. Main character multi-host energy. π
This is exactly what the automatic host and hostname values are for. Say you've got four servers named foo1, foo2, foo3, and foo4. And say you want the software to use a different database on each one. So app.conf might look like this:
[host=foo1] dbname = devel
[host=foo2]
dbname = test
[host=foo3]
dbname = prod
[ALL]
dbname = prod
Hopefully, that's enough to get you going. π¦
I welcome all feedback, bug reports, and feature requests.
π ACKNOWLEDGEMENTS π
- (c) 2010 Stephen Adkins
- Author: Stephen Adkins <spadkins@gmail.com>
- License: This is free software. It is licensed under the same terms as Perl itself.
π SEE ALSO π
Ngl, this section is just standing there, gorgeous and empty β there's nothing listed under it. (iykyk)
And that's a wrap on App::Options, bestie!! π
This page is App::Options(3pm), from the perl v5.34.0 era, dated 2022-06-08 β so if you're reading it on something wildly newer, no cap, just vibe check the source. Go forth and configure responsibly. π«ΆπΈβ¨
π¦ where this page comes from
- debian bookworm: libapp-options-perl 1.12-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libapp-options-perl 1.12-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libapp-options-perl 1.12-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libapp-options-perl 1.12-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libapp-options-perl 1.12-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms