Perl GetOptions,接受参数的选项触发子程序

4
我正在尝试使用来自GetOpt :: Long的GetOptions函数调用一个接受参数的子例程。但是,无论是否在命令行上指定选项,子例程都会被调用。如果在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 行内调用子例程的正确语法,并理解为什么会发生观察到的行为。


4
哇,今天我看到的第二个问题实际上包含一个 [mcve]。我很少在一天内看到两个这样的问题。恭喜! - Jim Garrison
2个回答

1
该模块需要一个代码引用,它可以使用子名称(\&name)或匿名子来获取;由于您不是在进行函数调用而是获取引用(到代码),因此没有"参数"的概念。然后在该代码中调用你的子程序。详细信息如下。
将选项与匿名子例关联,在其中可以调用你的子程序。
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()的参数。

问题中的调用并不是获取子例程引用的方式;您只需使用子例程名称\&name即可。这与Getopt无关,它只需要一个代码引用。

当您尝试“传递参数”时,它不再是coderef,而是执行该子函数,然后获取其返回的引用;与\sub()\( sub() )相同。这可以通过以下方式看到:

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

什么被打印

嗨
返回

虽然这不是一个获取引用的方式,但我仍要警告可能会出现的 意外情况(如果最终尝试获取“列表的引用”)


两个踩怎么回事?如果我疯了或者眼瞎了,请有人告诉我一声。 - zdim

1

我已经有几年没有使用Perl了,但我相信这是因为

\&sub1($var)

是指调用sub1结果。也就是说,这一行代码

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

实际上,在构建参数列表以调用 GetOptions 时,会调用 sub($var)。这似乎是语法中的一个特例,你正在获取该调用结果的引用。

这应该能够澄清问题:

$ 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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接