perl GetOptions,接受参数的选项触发子例程

时间:2017-11-15 03:36:16

标签: perl

我正在尝试使用GetOptions中的GetOpt::Long函数来调用接受参数的子例程。但是,无论是否在命令行上指定了该选项,都会调用子例程。如果参数未传递给GetOptions行中的子例程,则不会发生此意外行为。

以下是对问题的最小证明:

如果为GetOptions行中的子例程提供了一个参数,则子程序最终会被调用,无论它是否在命令行上提供了控制选项:

$ 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

相反,如果在没有参数的情况下在GetOptions中调用子例程,则表现得恰当:

$ 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

我做错了什么?

PS:我知道我可以简单地设置一个变量来控制是否在GetOptions块之后调用子例程,但是我想确定在GetOptions中调用子例程的正确语法。线,以及理解观察到的行为发生的原因。

2 个答案:

答案 0 :(得分:1)

模块expects a code reference,仅使用子名称(\&name)或匿名子;没有"争论的概念"因为你没有进行函数调用,而是获取引用(代码)。然后在该代码中调用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]"; }

或者使用'opt' => \&cb和子cb()来电sub1(...)。此回调传递选项名称和值(或者哈希值的名称,键和值),并且不带其他参数。因此,您无法以任何方式动态解析要传递给sub1()的参数。

问题中的调用不是如何获得subroutine reference;您只能使用子例程名\&name。这与Getopt无关,只需要代码参考。

当你尝试传递参数"这不再是一个coderef,但是执行了sub,然后引用了它的返回值;与\sub()\( sub() )相同。

可以看出这一点
perl -wE'sub tt { say "@_"; return "ret" }; $r = \&tt("hi"); say $$r'

打印什么

hi
ret

虽然这不是一种参考方式,但我仍然警告surprises

答案 1 :(得分:1)

我做了很多Perl已经有几年了,但我很确定这是因为

\&sub1($var)

是对调用sub1结果的引用。就是这一行

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

实际上调用sub($var)作为构造GetOptions的参数列表的一部分。这看起来像语法中的一个极端情况,您正在引用该调用的结果。

这应该清楚了:

$ 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