1

我正在使用 Perl CLI 框架编写一些脚本。现在我想将变量从控制模块传递到子命令模块。我尝试将变量设置为控制模块中的全局变量,但子命令模块仍然无法获取变量。它甚至无法在同一个模块中共享全局变量。执行脚本时有一些错误消息:

[root@old]# perl pc --ip=dsfa --device dsfasdf on 在连接 (.) 或 Power/Control.pm 第 63 行的字符串中使用未初始化的值 $Power::Control::data。

全局数据是使用未初始化的值 $Power::Control::data in concatenation (.) 或 Power/Control.pm 第 75 行的字符串。

来自父母的数据是

设备名称为 dsfasdf

ip地址是dsfa

这是上的命令

这是脚本电脑:

#! /usr/bin/perl

use strict;
use warnings;
use Power::Control;

use lib 'lib';

# ---- EXECUTION ----
Power::Control->run(); # Launch command

这是电源/Control.pm:

package Power::Control;
use base qw( CLI::Framework );

use strict;
use warnings;

sub usage_text {
    qq{
    $0 [--verbose|v]:

    OPTIONS:
        --verbose -v:   be vebose

    ARGUMENTS (subcommands):
        on:         power on the device
        off:        power off the device
        reboot:     reboot the device
        version:    show PDU version
        status:     show PDU status
        sysstat:    show PDU sysstatus
    }
}

sub option_spec {
    [ 'device|d=s'     => 'device name' ],
    [ 'ip=s'           => 'ip address' ],
    [ 'user|u=s'       => 'user name' ],
    [ 'password|p=s'   => 'password' ],
    [ 'interval|i=s'   => 'interval' ],
    [ 'brand|b=s'      => 'brand' ],
    [ 'community|c=s'  => 'community' ],
    [ 'version|v=s'    => 'version' ],
}

sub command_map {
    on      => 'Power::Control::Command::On',
    off     => 'Power::Control::Command::Off',
    reboot  => 'Power::Control::Command::Reboot',
    version => 'Power::Control::Command::Version',
    status  => 'Power::Control::Command::Status',
    sysstat => 'Power::Control::Command::Sysstat',
}

sub command_alias {
    r   => 'reboot',
    v   => 'version',
    st  => 'status',
    sys => 'sysstat',
}

our $opts;
our $self;
our $data;

sub init {
    ($self, $opts) = @_;

    $data = $opts->{'ip'};
    print "\n The device name is $opts->{'device'}\n";
    print "\n The ip address is $data\n";
}

print "\n The globla data is $data\n";
1;

# ---- COMMAND: On ----
package Power::Control::Command::On;
use base qw( CLI::Framework::Command );

use strict;
use warnings;
use Power::Control;
use Data::Dumper;

print "\n The data from parent is $data \n";

sub usage_text {
    q{
    on [--d=<device name>: Power on the device
    }
}

#sub option_spec {
#    [ 'device|d=s@'   => 'device name'  ],
#}

sub run {

    print "\n This is the command on\n";
}
1;
4

1 回答 1

0

感谢您跟进此事。我花了一两分钟将文件组织和分离成典型的lib/Power/Control.pm布局lib/Power/Control/Command/On.pm 并清理了一些东西,这是我得到的输出:

[localhost ~/tmp%]  perl stackex-pc --ip=127.0.0.1 --device=pants on
The device name is pants
The ip address is 127.0.0.1
This is the command "on" 

我认为这或多或少是你想要的。我将在下面添加我对我认为发生的事情的解释,但首先我将包含我的文件,您可以在家里尝试一下。如果它有效,那么您可以接受我的回答。如果我给出一个体面的解释,那么其他人可以投票给我!首先,这是脚本:

#! /usr/local/bin/perl
#
# stackex-pc 
# Question: http://stackoverflow.com/questions/17054798

use strict;
use warnings;

use lib 'lib' ;
use Power::Control;

# ---- EXECUTION ----
Power::Control->run(); # Launch command

这是包裹:

# lib/Power/Control.pm
# Question: http://stackoverflow.com/questions/17054798

package Power::Control;   
use base qw( CLI::Framework );
use strict;
use warnings;

our ($data, $opts, $self);

sub usage_text {
   qq{
   $0 [--verbose|v]:

OPTIONS:
    --verbose -v:   be vebose

ARGUMENTS (subcommands):
    on:         power on the device
    off:        power off the device
    reboot:     reboot the device
    version:    show PDU version
    status:     show PDU status
    sysstat:    show PDU sysstatus
    }
}

sub option_spec {
    [ 'device|d=s'     => 'device name' ],
    [ 'ip=s'           => 'ip address' ],
    [ 'user|u=s'       => 'user name' ],
    [ 'password|p=s'   => 'password' ],
    [ 'interval|i=s'   => 'interval' ],
    [ 'brand|b=s'      => 'brand' ],
    [ 'community|c=s'  => 'community' ],
    [ 'version|v=s'    => 'version' ],
}

sub command_map {
    on      => 'Power::Control::Command::On',
    off     => 'Power::Control::Command::Off',
    reboot  => 'Power::Control::Command::Reboot',
    version => 'Power::Control::Command::Version',
    status  => 'Power::Control::Command::Status',
    sysstat => 'Power::Control::Command::Sysstat',
}

sub command_alias {
    r   => 'reboot',
    v   => 'version',
    st  => 'status',
    sys => 'sysstat',
}

sub init {
    ($self, $opts) = @_;

    print "\n The device name is $opts->{device}\n";
    print "\n The ip address is $opts->{ip}\n";
}

1;

如前所述,我在模块目录类型布局中分离了文件,因此有第二个文件。我这样做是为了我自己的方便,所以不要觉得你必须做同样的事情。

# lib/Power/Control/Command/On.pm                                                                                   
# Question: http://stackoverflow.com/questions/17054798

package Power::Control::Command::On;        
use base qw( CLI::Framework::Command );     
use strict;                                                                      
use warnings;                                                                    
use Power::Control;                                                              
                                                                            
sub usage_text {                                                                 
    q{                                                                           
    on [--d=<device name>: Power on the device
    }                                                                            
}                                                                            
                                                                            
sub run {                                                                    
    print "\n This is the command \"on\" \n";
    exit ;
}   
1;         

我认为您的第一种方法不起作用的原因是 - 正如错误消息所示 - 未初始化的变量。我尝试的解释如下:

  1. 我需要放置use lib 'lib'; 之前, use Power::Control;否则脚本将没有完整的 @INC 并且无法找到Power::Control. 当我解决这个问题时,我得到了和你一样的错误。这表明您可能已经以不同方式设置了 CLI::Framework,或者将您自己的模块放在了 @INC 的某个位置。

  2. 如果您"our $data ; 在第 53 行更改lib/Power/Control.pm our $data = "in the global scope"; - 否则未定义 - 您将在运行脚本时看到该消息。"$data" 值没有从 init() 子例程内部设置 如果您print "\n The ip address is $data\n";在第 60 行附近的 init() 子例程中替换为lib/Power/Control.pmprint "\n The ip address is $opts->{ip}\n";您将获得更符合您预期的输出。$data = 如果您更改内部init()以相应地读取my $otherdata = ...和调整print行,您还将了解变量的不同词法值的影响。

  3. 我还没搞定!我将查看/编辑我的解释并添加一些关于变量范围以及$data变量在此CLI::Framework应用程序中的作用的参考。

顺便说一句,这很棘手,但是您可以通过在 perl 调试器中运行脚本来解决很多问题,只需将-d开关添加到您的命令:

perl -d pc --ip=dsfa --device dsfasdf on.

然后在脚本运行时单步执行,尝试转储变量值、跟踪等。

如果您尝试将on命令参数放在选项之前--ip=,您会看到另一种问题:命令行参数解析有点脆弱。您可能还可以添加一个优雅的检查来解决这个问题,或者至少使它Failed parsing of command options更有意义(例如,将使用屏幕发送给用户)。

祝你的项目好运。

于 2013-06-16T00:01:50.563 回答