Search code examples
perlreferencesubroutine

perl GetOptions, option-triggered subroutine that accepts arguments


I am trying to use the GetOptions function from GetOpt::Long to call a subroutine that accepts an argument. However, the subroutine gets called regardless of whether the option is specified on the command line. This unexpected behavior does not occur if an argument is not passed to the subroutine in the GetOptions line.

What follows is a minimal demonstration of the problem:

If an argument is provided to the subroutine in the GetOptions line, the subroutine ends up being called regardless of whether its controlling option is supplied on the command line:

$ cat a1.pl
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
my $var="entered";
GetOptions ( "opt" => \&sub1($var) );
sub sub1 { print "sub1 $_[0]\n"; }

$ perl a1.pl --opt
sub1 entered

$ perl a1.pl
sub1 entered

In contrast, if the subroutine is called in GetOptions without an argument, it behaves appropriately:

$ cat a2.pl
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
GetOptions ( "opt" => \&sub2 );
sub sub2 { print "sub2 entered\n"; }

$ perl a2.pl --opt
sub2 entered

$ perl a2.pl

What am I doing wrong?

PS: I know that I can simply set a variable that controls whether the subroutine is called after the GetOptions block, but I would like to determine the correct syntax for calling the subroutine within the GetOptions line, as well as understand why the observed behavior is happening.


Solution

  • The module expects a code reference, and that is taken with the sub name alone (\&name) or as an anonymous sub; there is no notion of "arguments" as you are not making a function call but rather obtaining a reference (to code). Then call you sub in that code. Details follow.

    Associate the option with an anonymous subroutine, inside of which you can call your sub

    use warnings;
    use strict;
    use feature 'say';
    
    use Getopt::Long;
    
    my $opt;
    my $var = 'entered';
    
    GetOptions ( 'opt' => sub { $opt = 1; sub1($var) } );
    
    sub sub1 { say "sub1 $_[0]"; }
    

    Or use 'opt' => \&cb and in the sub cb() call sub1(...). This callback is passed the option name and value (or name, key, and value in case of a hash), and doesn't take other arguments. So you cannot in any way dynamically resolve what arguments to pass to sub1().

    The invocation in the question is not how a subroutine reference is obtained; you must only use the subroutine name, \&name. This is not about Getopt, which just wants a code reference.

    When you attempt to "pass arguments" that isn't a coderef anymore but the sub is executed and then the reference taken of its return; same as \sub() or \( sub() ). This can be seen by

    perl -wE'sub tt { say "@_"; return "ret" }; $r = \&tt("hi"); say $$r'
    

    what prints

    hi
    ret
    

    While this just isn't a way to take a reference let me still warn of possible surprises (if one ends up trying to take a "reference to a list")