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

Hello, I am new to Perl and will be very grateful for your patience and assistance.

I am looking for some advice on how to include named args when constructing a new object only if they were specified on the command line.

I am writing a script that uses the Net::SNMP module and I want to have certain optional args, e.g. privpassword and privprotocol, only included when I create the new Net::SNMP object if the user specified a value for them on the command line other than "" (un-used args will be quoted empty, i.e. "").

The script is meant to be called by another program (it's a Cacti plugin FWIW) with position-dependent command line arguments. Here is an example of how my script would be used:

check_snmp.pl 10.0.0.9 admin mypassword https public 2c 161 2500 10 myauthuser myauthpassword sha "" "" "" query portid

My script looks something like this (please note that $user, $pass and $protocol are irrelevant to this question):

use strict; use warnings; use Net::SNMP qw(:snmp); my $address = $ARGV[0]; my $user = $ARGV[1]; my $pass = $ARGV[2]; my $protocol = lc $ARGV[3]; my $snmp_community = $ARGV[4]; my $snmp_version = $ARGV[5]; my $snmp_port = $ARGV[6]; my $snmp_timeout = $ARGV[7]; my $max_oids = $ARGV[8]; my $snmp_username = $ARGV[9]; my $snmp_password = $ARGV[10]; my $snmp_auth_protocol = $ARGV[11]; my $snmp_priv_passphrase = $ARGV[12]; my $snmp_priv_protocol = $ARGV[13]; my $snmp_context = $ARGV[14]; my $action = lc $ARGV[15]; my $type = lc $ARGV[16]; my ( $session, $error ) = Net::SNMP->session( hostname => $address, port => $snmp_port, version => "snmpv" . $snmp_version, timeout => $snmp_timeout, debug => $snmp_debug_mask, username => $snmp_username, authpassword => $snmp_password, authprotocol => $snmp_auth_protocol, privpassword => $snmp_priv_passphrase, privprotocol => $snmp_priv_protocol, );

What I would really like to know is if there are any ways of only including the named arguments when they have been given a value other than "" on the command line?

I tried using an inline if statement, which failed, e.g.

my ( $session, $error ) = Net::SNMP->session( ... if ($snmp_priv_passphrase ne "") { privpassword => $snmp_priv_pas +sphrase, } ... }

I tried using the arguments undefined, which also failed, e.g.

if ($snmp_priv_passphrase ne "") { $snmp_priv_passphrase = undef; } my ( $session, $error ) = Net::SNMP->session( ... }

I know I could put the whole code block inside a series of if statements, so that the named arguments are only included when they have been given a value other than "", but I was wondering if there was a better way of doing it?

Thanks for any help.

Replies are listed 'Best First'.
Re: Optionally include named arguments when constructing a new object
by boftx (Deacon) on Jun 02, 2014 at 04:49 UTC

    Try using a hash with the named params as keys like this:

    my %session_params = ( hostname => $ARGV[0], port => $ARGV[6], version => $ARGV[5] ? "snmpv" . $ARGV[5] : '', timeout => $ARGV[7], ... # and so on for all params }; for ( keys( %session_params ) { delete( $session_params{$_} unless $session_params{$_}; } my ( $session, $error ) = Net::SNMP->session( %session_params ); # Update: # allowed for an empty string for an arg that was being manipulated: $ +ARGV[5]
    This is a very simple example, but I think you get the idea. You can see how I use a similar approach which includes setting default values if you view the source for DateTimeX::Fiscal::Fiscal5253.

    I would consider using Getopt::Long and have named params on the command line to make life a little bit easier.

    Update: Here is the direct solution to what you gave:

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $foo = 1; my $bar = 'baz'; my $baz = ''; my %foohash = ( $foo ? ( foo => $foo ) : (), bar => $bar, $baz ? ( baz => $baz ) : (), $ARGV[0] ? ( version => 'snmpv' . $ARGV[0] ) : () ); print Dumper(\%foohash); exit; __END__ Output: $VAR1 = { 'bar' => 'baz', 'foo' => 1 };

    No matter what approach you take, I strongly advise doing some parameter validation before using them!

    You must always remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
Re: Optionally include named arguments when constructing a new object
by tobyink (Canon) on Jun 02, 2014 at 12:46 UTC

    Here's some ideas...

    use strict; use warnings; { package Fake::Object; use Data::Dumper; sub new { shift; print Dumper +{ @_ }; } } my $var1 = "foo"; my $var2 = ""; # As you might expect... # Fake::Object->new( var1 => $var1, var2 => $var2, ); # Use the so-called "enterprise operator" # Fake::Object->new( var1 => $var1, (var2 => $var2) x!!( $var2 ne "" ), ); # PerlX::Maybe has a "provided" function which, # if the first argument is true returns the other # two arguments, and if the first argument is # false returns the empty list. # use PerlX::Maybe qw( provided ); Fake::Object->new( var1 => $var1, provided $var2 ne "", var2 => $var2, );
    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
Re: Optionally include named arguments when constructing a new object
by jellisii2 (Hermit) on Jun 02, 2014 at 13:00 UTC
    If you're not completely locked in to your input parameters, Getopt::Long may be useful to you.
    use strict; use warnings; use Getopt::Long; my %opts; GetOptions( 'address=s' => sub { $opts{$_[0]} = $_[1]; } 'user=s' => sub { $opts{$_[0]} = $_[1]; } 'pass=s' => sub { $opts{$_[0]} = $_[1]; } 'protocol=s' => sub { $opts{$_[0]} = $_[1]; } 'snmp_community=s' => sub { $opts{$_[0]} = $_[1]; } 'snmp_port=s' => sub { $opts{$_[0]} = $_[1]; } 'snmp_timeout=s' => sub { $opts{$_[0]} = $_[1]; } ... }
    Getopt::Long can be set to carp/croak when your script doesn't have all the required items, which may come in handy as well.
Re: Optionally include named arguments when constructing a new object
by nice_iguana (Initiate) on Jun 03, 2014 at 02:43 UTC

    Hi,

    boftx, thanks for the advice, I ended up basing my code on your "direct solution".

    tobyink, thanks for pointing me towards the so-called "enterprise operator" ()x!! I found Aristotle's article about it (Secret Perl Operators: the boolean list squash operator, x!!). The article and the subsequent comments were very interesting.

    jellisii2, thanks.

    I had originally used Getopt::Long but my script is intended to be used with a Cacti 'script query' and it seems to me that it is better to use position dependent command line parameters in this case. Admittedly this is based on my fairly limited understanding of both Cacti and Perl. The notes on arg_prepend in The Complete XML File section in the Cacti script data query walkthrough show what I am attempting.