Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

[General] Non-Specific Options Question

by arblargan (Acolyte)
on Aug 02, 2017 at 06:56 UTC ( #1196515=perlquestion: print w/replies, xml ) Need Help??

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

Monks, I have a question regarding options and their use in Perl. The nature of my question is more academic and not necessarily context specific. In fact, I'm not even sure that options is the proper term I'm looking for.

I'm simultaneously learning Perl and creating my own modular program to decode a complex diagnostic file. This program, once completed will be used by many people across many machines. As such, there are going to be certain facets of the program that I'd like to be configurable based on user preference. An extremely generic description of the program is that it will take a raw data file and output the information to an Excel spreadsheet.

Regarding the user configurable options, I'd like for the user to be able to choose whether or not they want the Excel file color coded, whether they want certain sheets in the Excel file, directories for output file storage, etc... Ideally, I'd like to have a user config file of sorts where each user can set their default values on how they'd like to program to handle these steps of processing. Are these examples of something that would be handled through the use of ARGV passed arguments/options?

If so, could anyone help me wrap my head around these uses and how to implement them in code? I've read several articles and tutorials about options/arguments, but everything seems to be command line centric. I just can't seem to connect the dots. If there's any other resources that might help me understand these uses, I would greatly appreciate it. Thanks!

  • Comment on [General] Non-Specific Options Question

Replies are listed 'Best First'.
Re: [General] Non-Specific Options Question
by Discipulus (Canon) on Aug 02, 2017 at 07:31 UTC
    Hello arblargan,

    Command line centric? well if you do not like this you can easily go for configuration file option.

    Config::General and Config::Any can be valid options and if you want configuration in JSON you can go with JSON::MaybeXS

    But really you do not have to choose between command line options versus configuration file: you can have both!

    I'm really a fan of The Dynamic Duo --or-- Holy Getopt::Long, Pod::UsageMan! and I suggest you to use this approach as base. Then you can specify the possibility to import the configuration via config file overriding option passed via command line: put this very clearly stated in the documentation and if possible warn the user about this behaviour of your application.

    You can also use Modules as configuration files if it is the case.

    As pseudocode I suggest something like:

    # declare all aribles used by arguments or config files soon, specifyi +ng their default values: my $arg_color = 'red'; my $arg_limit = 1024; my $arg_config; # use Getopt::Long to parse command line arguments: die if this goes b +ad GetOptions( 'color=s' => \$arg_color, 'limit=i' => \$arg_limit , 'configuration => \$arg_config, ) or pod2usage(-verbose => 1) && exit; # apply eventual configuration if ($arg_config){ # warn about option that be ovveride and import the configuration ... } # check that all options are valid warning which one is incorrect, ret +urn false and die &check_config or die "Options are invalid";

    L*

    PS you can be also see default option How do I process many (conflicting) command line parameters? GetOpt Organization and getopt::std to pass arguments to command line help to find some inspiration

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: [General] Non-Specific Options Question
by kcott (Archbishop) on Aug 02, 2017 at 09:18 UTC

    G'day arblargan,

    There's probably some options you'll want to hard-code such as '--version' and '--help'. These typically run some very small part of the code and then terminate: the main application wouldn't be run. They are options without values whose operations you wouldn't want your users to change; although, you may allow some output modifications with options like '--verbose' and '--full'.

    There may be some default values that you might want to hard-code or place in some sort of master configuration file. These could be overridden by user choices in a user configuration file.

    You may want to allow users to set up options in an environment variable. These might be specific to a client, project, and so on: not general enough for a config file but a nuisance to restate for every run.

    Then you might want command line options: items that users want to change on a run-by-run basis.

    If your application is interactive, perhaps users can change options in mid-run.

    I see ++Discipulus has provided some information on config files. I won't elaborate on that.

    Here's some code that allows users to change starting settings with values from an environment variable; and then to override those with command line options.

    #!/usr/bin/env perl -l use strict; use warnings; use Getopt::Long 'GetOptionsFromString'; my ($x, $y, $z) = qw{42 fred 1.0}; my %opts = ('x=i', \$x, 'y=s', \$y, 'z=f', \$z); print 'Option starting values:'; print "Options: x[$x] y[$y] z[$z]"; if (exists $ENV{USER_OPTS}) { GetOptionsFromString($ENV{USER_OPTS}, %opts); print 'After environment variable options processed:'; print "Options: x[$x] y[$y] z[$z]"; } if (@ARGV) { GetOptions(%opts); print 'After command line options processed:'; print "Options: x[$x] y[$y] z[$z]"; }

    Here's a few sample runs to show that in action.

    $ pm_1196515_option_processing.pl Option starting values: Options: x[42] y[fred] z[1.0] $ export USER_OPTS='-z 1.5' $ pm_1196515_option_processing.pl Option starting values: Options: x[42] y[fred] z[1.0] After environment variable options processed: Options: x[42] y[fred] z[1.5] $ pm_1196515_option_processing.pl -x 21 Option starting values: Options: x[42] y[fred] z[1.0] After environment variable options processed: Options: x[42] y[fred] z[1.5] After command line options processed: Options: x[21] y[fred] z[1.5] $ pm_1196515_option_processing.pl -y wilma -z 3.4 Option starting values: Options: x[42] y[fred] z[1.0] After environment variable options processed: Options: x[42] y[fred] z[1.5] After command line options processed: Options: x[42] y[wilma] z[3.4]

    If you want to give users the ability to change options in mid-run, here's some code I posted a few months ago to do that: "Re: How to process Getopt::Long from STDIN".

    — Ken

Re: [General] Non-Specific Options Question
by Corion (Patriarch) on Aug 02, 2017 at 08:45 UTC

    Update: For a more concrete application of the idea which is not as overdesigned as my approach, see kcott's reply.

    I like the following cascade of options, but if you are just starting out with your program, I recommend that you go with the config file alone, or maybe config file + defaults. Here are the overkill steps to do that:

    1. Set up defaults:
      sub get_defaults { return +{ output_type => 'excel', output_dir => '/tmp', ... }; };
    2. Read a config file, maybe JSON or YAML. YAML is supposed to be "user-friendly" (which I think it isn't), but it's commonly used. JSON is a contender but it doesn't allow comments, which I think are important in a config file.
      use YAML 'LoadFile'; sub config_from_file { my( $filename ) = @_; LoadFile( $filename ) }
    3. Use switches from the environment:
      sub config_from_ENV { my ( $env ) = @_; { output_type => $env->{MYAPP_output_type}, ... } }
    4. Use command line switches:
      sub config_from_ARGV { my( $argv ) = @_; GetOptionsFromArray ( 'f|config_file:s' => \my $config_file, 't|output_type:s' => \my $output_type, ); return +{ config_file => $config_file, output_type => $output_type, },
    5. Merge all the values, starting from the default upwards:
      my $defaults = get_defaults(); my $argv = config_from_ARGV( \@ARGV ); my $config_file = $argv->{config_file} || 'myconfig.yml'; my $file = config_from_file( $config_file ); my $env = config_from_ENV( \%ENV ); # Merge the things my $final_config = $defaults; for my $cfg ($config_file, $env, $argv) { for my $entry (keys %$cfg) { if(defined $cfg->{$entry}) { $final_config->{ $entry } = $cfg->{ $entry }; }; }; };
    6. Done

    For a start, I would only implement get_defaults and config_from_ARGV, or even skip one of the two.

Re: [General] Non-Specific Options Question
by karlgoethebier (Abbot) on Aug 03, 2017 at 07:41 UTC

    You could make a decision, as described in Re: How do I process many (conflicting) command line parameters? (and modify it a bit):

    # untested my $action = shift; my %actions = ( options => \&get_options, config => \&read_config ); $actions{$action}->(); sub get_options { # get options } sub read_config { # read config }

    Regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

    perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2023-06-06 20:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How often do you go to conferences?






    Results (29 votes). Check out past polls.

    Notices?