如何使用Perl CLI Framework共享两个模块之间的全局变量?

时间:2013-06-11 22:17:53

标签: perl frameworks command-line-interface

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

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

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

来自父母的数据是

设备名称为dsfasdf

ip地址是dsfa

这是

上的命令

这是脚本pc:

#! /usr/bin/perl

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

use lib 'lib';

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

这是Power / 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;

1 个答案:

答案 0 :(得分:0)

感谢您对此采取后续行动。我花了一两分钟来组织和分离文件到一个典型的lib/Power/Control.pmlib/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. 我需要在{/ em> use lib 'lib';之前放置use Power::Control; ,否则脚本将没有完整的@INC而无法找到Power::Control。当我解决这个问题时,我得到了和你一样的错误。这表明您可能以不同的方式设置CLI :: Framework,或者将自己的模块放在@INC中的某个位置。

  2. 如果您更改"our $data ; lib/Power/Control.pm的第53行上的our $data = "in the global scope"; - 否则未定义 - 您将在运行脚本时看到该消息。 “$ data”值未从init()子例程内部设置如果在print "\n The ip address is $data\n";中使用lib/Power/Control.pm的第60行的init()子例程中替换print "\n The ip address is $opts->{ip}\n";,您将获得更像您期望的输出。如果您将$data =内的init()更改为my $otherdata = ...并相应地调整print行,您还可以了解变量的不同词汇值的效果。

  3. 我还没完成!我将审核/编辑我的解释,并添加一些有关变量范围的引用,以及$data变量在此CLI::Framework应用内所做的内容。

  4. 顺便说一下,这很棘手,但你可以通过在perl调试器中运行脚本来简单地找出很多,只需在你的命令中添加-d开关:

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

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

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

    祝你的项目好运。