in reply to Passing Parameters to subroutine

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

Replies are listed 'Best First'.
Re^2: Passing Parameters to subroutine
by g_speran (Scribe) on Mar 18, 2022 at 02:14 UTC
    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.
Re^2: Passing Parameters to subroutine
by g_speran (Scribe) on Mar 18, 2022 at 00:58 UTC
    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