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

uz75evzq  于 2022-11-15  发布在  Perl
关注(0)|答案(2)|浏览(213)

我试图使用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行中调用子例程的正确语法,并了解为什么会出现观察到的行为。

t8e9dugd

t8e9dugd1#

该模块需要一个 * 代码引用 *,并且该引用单独与子名称(\&name)一起使用或作为匿名子对象使用;这里没有“参数”的概念,因为你不是在进行函数调用,而是在获取一个引用(对代码)。2然后在那个代码中调用你的子函数。3详细信息如下。
将选项与匿名子例程关联,在该子例程中可以调用子例程

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'

打印内容

hi
ret

虽然这不是一种获取引用的方法,但我仍然要警告可能的surprises(如果最终试图获取“对列表的引用”)

osh3o9ms

osh3o9ms2#

我已经有几年没有使用过Perl了,但我非常肯定这是因为

\&sub1($var)

是对调用sub1result 的引用。也就是说,

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

相关问题