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

I am using Getopt::Long. When passing a parameter via the command line, getopts seems to pass the parmeter on the cli followed by a 1 to the identified subroutine. I would be expecting, when the subroutine is called from the option passed from CLI, the parameters passed to the actual subroutine would be null/empty. when passing parameter to a subtroutine from within the script, the expected parameters are passed and printed. Any idea or assistance on how to achieve my expected results?
#!/usr/bin/perl use strict; use Getopt::Long; GetOptions ('demo' => \&demo, 'test' => \&Test) or die("Error in command line arguments\n"); sub demo () { die "Wrong number of args" if (scalar(@_) != 2); my $arg0 = $_[0]; my $arg1 = $_[1]; my $arg2 = $_[2]; my $arg3= $_[3]; my $arg4 = $_[4]; my $arg5 = $_[5]; print "$arg0, $arg1, $arg2, $arg3, $arg4, $arg5\n"; } sub Test () { # call the subroutine my $arg0 = $_[0]; my $arg1 = $_[1]; my $arg2 = $_[2]; my $arg3= $_[3]; my $arg4 = $_[4]; my $arg5 = $_[5]; print "$arg0, $arg1, $arg2, $arg3, $arg4, $arg5\n"; &demo("hello", "world"); } C:\temp>passparm.pl --demo demo, 1, , , , <== would expect nothing to print C:\temp>passparm.pl --test test, 1, , , , <== would expect nothing to print hello, world, , , , <== Expected C:\temp>passparm.pl --demo --test demo, 1, , , , <== would expect nothing to print test, 1, , , , <== would expect nothing to print hello, world, , , , <== Expected

Replies are listed 'Best First'.
Re: Passing Parameters to subroutine
by LanX (Saint) on Mar 18, 2022 at 00:24 UTC
    Your code has many issues and I don't think the output corresponds to the code shown.

    Most importantly don't use empty prototypes () and use warnings

    Anyway this works for me and avoids printing undefined values.

    use strict; use warnings; use Getopt::Long; GetOptions( 'demo' => \&demo, 'test' => \&test ) or die("Error in command line arguments\n"); sub demo { my ( $arg0, $arg1, $arg2, $arg3, $arg4, $arg5 ) = @_ ; warn "Number of args: ", scalar @_; die "expected 2 args" if @_ != 2; warn join ",",@_; } sub test { die "expected 2 args" if @_ != 2; warn join ",",@_; demo("hello", "world"); }
    OUTPUT:
    test,1 at d:/tmp/pm/getopt_long.pl line 23, <DATA> line 26. Number of args: 2 at d:/tmp/pm/getopt_long.pl line 16, <DATA> line 26. hello,world at d:/tmp/pm/getopt_long.pl line 18, <DATA> line 26.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      Resolved
      GetOptions( 'demo' => sub{demo();}, 'test' => sub{test();} ) or die("Error in command line arguments\n");
        I don't think that you understood what the Monks are saying. This is not "resolved". Do not use GetOpts in this way because amongst other things, you do not get proper checking of the input command line for errors. The entire command line should be checked for errors before you do anything. Calling a sub by GetOpts is part of command line checking and you don't need that to do what you want to do - or my understanding of what you want to do.

        Here is one possible implementation:

        use strict; use warnings; use Getopt::Long; my $demo = 0; # default value (false) my $test = 0; # default value (false) GetOptions ('demo' => \$demo, 'test' => \$test); ### Once command line is parsed using GetOpts, THEN execute code ### based upon the flags which are set. ### GetOpts removes what it knows about from @ARGV - if there is ### anything left, then you have a syntax error. ### It is up to you to check for contradictory options, like ### perhaps asking for --demo and --test at the same time! if (@ARGV) { print "Illegal syntax \'@ARGV\' is bogus!\n"; printHelp(); } if (!($demo or $test)) { print "No option specified!\n"; printHelp(); } if (!($demo xor $test)) #test for inconsistent options { print "Only one option allowed!\n"; printHelp(); } sub printHelp { print "*** print something useful for help message**\n"; exit (1); #error exit to shell - the command "didn't work" } ### actual "code" is here ### demo() if $demo; #simple for this scenario test() if $test; sub demo { print "doing a demo\n"; } sub test { print "doing a test\n"; } __END__ example runs: >perl longopts.pl No option specified! *** print something useful for help message** >perl longopts.pl -d abc Illegal syntax 'abc' is bogus! *** print something useful for help message** >perl longopts.pl -d -t Only one option allowed! *** print something useful for help message** >perl longopts.pl adf Illegal syntax 'adf' is bogus! *** print something useful for help message** >perl longopts.pl -d doing a demo >perl longopts.pl -t doing a test
        How to get GetOpts to parse say: command --test 23 --fancyprint is more complicated than the above syntax, but certainly possible.
      i believe you got the same result i Did. on you first run you passed the parameter "test" on the cli, i assume. within the test subroutine, it printed "test,1", as identified on your very first line of output. i would not want or be expecting "test,1" since i specifically did not pass the parameter test or 1 to the subroutine test

        You seem to have some . . . let's say misconceptions about what Getopt::Long is for and how to use it. When you pass a sub ref to it you're telling it "Hey, if you see option --test then call this sub". So since you're passing that option, that sub gets called. As is documented, passing a sub is meant to allow more complicated behaviors when receiving an option than (say) "set what this scalar ref points to to the next command line argument" and the sub will receive the option name it's being called for and the value (1, the option was present) as the arguments.

        It's not meant to be used to wire up program logic directly.

        What you want to be doing would be setting flag variables (start off with my $call_test = undef and then passing ... "test" => \$call_test ...) which then AFTER you've let Getopt::Long parse the options you check and conditionally call whatever subs.

        (Also don't add empty paren prototypes willy nilly in your code; they don't do anything useful in this context and would actaully break in normal code calling subs normally if you then try and pass arguments (because you've said they take none).)

        Edit: Emphasis added because it's not sinking in.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

        > i would not want or be expecting "test,1" since i specifically did not pass the parameter test or 1 to the subroutine test

        That's how it is supposed to work. You must distinguish between input subs - i.e. get-opt-handlers - and processing subs.

        from Getopt::Long#User-defined-subroutines-to-handle-options

          When GetOptions() encounters the option, it will call the subroutine with two or three arguments. The first argument is the name of the option.

          ... For a scalar or array destination, the second argument is the value to be stored.

          For a hash destination, the second argument is the key to the hash, and the third argument the value to be stored. It is up to the subroutine to store the value, or do whatever it thinks is appropriate.

        Please note that it's possible to call --option arg1 arg2 arg3 and these arguments need to be processed via 'test=s@' => \&test

        In your case of a scalar option, the meaning of "test 1" is

        • "test" is the name of the option, because you can register the same sub to multiple different options.
        • "1" is the default true value for activation of this option
        That's consistent with a use case of "test!" => \&test where --no-test will result in a call with test 0 .

        use strict; use warnings; use Getopt::Long; GetOptions( 'demo' => \&demo, 'test!' => \&test ) or die("Error in command line arguments\n"); sub test { die "expected 2 args" if @_ != 2; warn join ",",@_; # demo("hello", "world"); } __DATA__
        OUTPUT:
        C:/Strawberry/perl/bin\perl.exe -w d:/tmp/pm/getopt_long.pl --no-test test,0 at d:/tmp/pm/getopt_long.pl line 16, <DATA> line 28.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Passing Parameters to subroutine
by haukex (Archbishop) on Mar 18, 2022 at 05:41 UTC
    When passing a parameter via the command line, getopts seems to pass the parmeter on the cli followed by a 1 to the identified subroutine. I would be expecting ...

    This is documented behavior, since it's a boolean option the 1 represents that the option was passed. Also, as discussed last time, the only thing the subroutines called by Getopt::Long should be doing is storing the fact that the options were passed to the program. If you use the module as intended, I think you'll have an easier time.

A reply falls below the community's threshold of quality. You may see it by logging in.