user001
user001

Reputation: 1848

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.

Upvotes: 4

Views: 1549

Answers (2)

zdim
zdim

Reputation: 66881

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")

Upvotes: 1

Jim Garrison
Jim Garrison

Reputation: 86774

It's been a few years since I did much Perl, but I'm pretty sure it is because

\&sub1($var)

is a reference to the result of invoking sub1. That is, the line

GetOptions ( "opt" => \&sub1($var) );

actually invokes sub($var) as part of constructing the argument list to GetOptions. This looks like a corner case in the syntax, you are taking a reference to the result of that invocation.

This should clear things up:

$ perl -de0

Loading DB routines from perl5db.pl version 1.49_001
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(-e:1):   0
  DB<1> sub sub1 { print "sub1\n"; }

  DB<2> sub1()
sub1

  DB<3> &sub1
sub1

  DB<4> \&sub1

  DB<5> x \&sub1
0  CODE(0x804d7fe8)
   -> &main::sub1 in (eval 6)[/usr/lib/perl5/5.22/perl5db.pl:737]:2-2
  DB<6> x \&sub1()
sub1
0  SCALAR(0x804ee7f0)
   -> 1

Upvotes: 1

Related Questions