【问题标题】:I get error "variable will not stay shared" when i use subroutine使用子例程时出现错误“变量不会保持共享”
【发布时间】:2020-04-13 11:13:14
【问题描述】:

我写了一个函数,这里是:

use strict;
use warnings;
use feature 'say';
use JSON;
use utf8;

sub process {
    my %IDs = ( "User awx01 logged in." => 1001 );
    my %levels = ( INFO => 4 );
    my $data = do { local $/; <DATA> };

    # read in all the data, even though it looks

    my $decoded = decode_json( $data );
    $decoded->{Message} = decode_json( $decoded->{Message} );

    say rec2msg($decoded);

    sub rec2msg {
        my $r = shift;
        $r->{Message}{message} =~ /(\w+) (\w+) (.+)/;

        my($user,$msg) = ($2,"$1 $3");
        my $ID    = $IDs{$r->{Message}{message}};
        my $level = $levels{$r->{Message}{level}};

        my $out = "$r->{Message}{'@timestamp'} host CEF:0|OpenSource|AWX|7.0.0|$ID|$msg|$level|src=127.0.0.1 dst=$r->{MessageSourceAddress} duser=$user";
        return $out;
    }

}
__DATA__
{"MessageSourceAddress":"192.168.81.20","EventReceivedTime":"2020-02-06 11:55:14","SourceModuleName":"udp","SourceModuleType":"im_udp","SyslogFacilityValue":1,"SyslogFacility":"USER","SyslogSeverityValue":5,"SyslogSeverity":"NOTICE","SeverityValue":2,"Severity":"INFO","EventTime":"2020-02-06 11:55:14","Hostname":"192.168.81.20","Message":"{\"@timestamp\": \"2020-02-06T08:55:52.907Z\", \"message\": \"User awx01 logged in.\", \"host\": \"awxweb\", \"level\": \"INFO\", \"logger_name\": \"awx.api.generics\", \"stack_info\": null, \"type\": \"other\", \"cluster_host_id\": \"awx-contr-01\", \"tower_uuid\": \"333b4131-495f-4460-8e4b-890241a9d73d\"}"}

运行此代码后所需的输出是:

2021-02-06T08:55:52.907Z host CEF:0|OpenSource|AWX|7.0.0|1001|User logged in.|4|src=127.0.0.1 dst=192.168.81.20 duser=awx01

但是,当我运行此代码时,我收到此错误:

变量“%IDs”不会在 /usr/libexec/nxlog/modules/extension/perl/event1.pl 第 25 行保持共享。 变量“%levels”不会在 /usr/libexec/nxlog/modules/extension/perl/event1.pl 第 26 行保持共享。

我该如何解决?我真的需要它在一个功能中

我试过了,但没有用:

use strict;
use warnings;
use feature 'say';
use JSON;
use utf8;

sub process {
    my %IDs = ( "User awx01 logged in." => 1001 );
    my %levels = ( INFO => 4 );
    my $data = do { local $/; <DATA> };

    # read in all the data, even though it looks

    my $decoded = decode_json( $data );
    $decoded->{Message} = decode_json( $decoded->{Message} );

    say rec2msg($decoded);

    local *rec2msg = sub {
        my $r = shift;
        $r->{Message}{message} =~ /(\w+) (\w+) (.+)/;

        my($user,$msg) = ($2,"$1 $3");
        my $ID    = $IDs{$r->{Message}{message}};
        my $level = $levels{$r->{Message}{level}};

        my $out = "$r->{Message}{'@timestamp'} host CEF:0|OpenSource|AWX|7.0.0|$ID|$msg|$level|src=127.0.0.1 dst=$r->{MessageSourceAddress} duser=$user";
        return $out;
    }
    return rec2msg();


}
__DATA__
{"MessageSourceAddress":"192.168.81.20","EventReceivedTime":"2020-02-06 11:55:14","SourceModuleName":"udp","SourceModuleType":"im_udp","SyslogFacilityValue":1,"SyslogFacility":"USER","SyslogSeverityValue":5,"SyslogSeverity":"NOTICE","SeverityValue":2,"Severity":"INFO","EventTime":"2020-02-06 11:55:14","Hostname":"192.168.81.20","Message":"{\"@timestamp\": \"2020-02-06T08:55:52.907Z\", \"message\": \"User awx01 logged in.\", \"host\": \"awxweb\", \"level\": \"INFO\", \"logger_name\": \"awx.api.generics\", \"stack_info\": null, \"type\": \"other\", \"cluster_host_id\": \"awx-contr-01\", \"tower_uuid\": \"333b4131-495f-4460-8e4b-890241a9d73d\"}"}

【问题讨论】:

    标签: perl subroutine


    【解决方案1】:

    使用嵌套的命名子程序有点不寻常。

    The documentation 为该错误消息提供了一些建议:

    变量“%s”不会保持共享状态

    (W 闭包)一个内部(嵌套)命名子例程正在引用一个在外部命名子例程中定义的词法变量。

    当内部子程序被调用时,它将看到外部子程序变量的值,就像在第一次调用外部子程序之前和期间一样;在这种情况下,在对外部子例程的第一次调用完成后,内部子例程和外部子例程将不再共享该变量的公共值。换句话说,该变量将不再被共享。

    这个问题通常可以通过使用 sub {} 语法使内部子例程匿名来解决。当创建引用外部子例程中变量的内部匿名子时,它们会自动重新绑定到这些变量的当前值。

    所以你可以遵循这个建议,或者完全摆脱内部子程序。一眼看去,我看不出有什么真正的原因。

    【讨论】:

    • 你能用代码回答吗?我是 perl 的超级新手)
    • @irate_mastermind 不要使用那种奇怪的嵌套rec2msg 子例程,只需直接在process 中执行其操作即可。您必须删除三行并更改两行。应该很容易弄清楚。
    • 最不清楚的部分是如何处理:my $decoded = decode_json( $data ); $decoded->{Message} = decode_json( $decoded->{Message} );说 rec2msg($decoded);如果我将内容从 rec2msg 转移到 process 函数,该怎么办?
    • @irate_mastermind 您只需在一个函数中执行相同的操作,而不是尝试将其分成两个。我什至不明白你是如何找到你正在尝试的东西而不是直接去做的。
    【解决方案2】:

    在代码顶部声明以下变量,例如,

    my (%IDs,%levels) = ();
    

    把这里声明的变量去掉,

    my %IDs = ( "User awx01 logged in." => 1001 ); #instead of my %IDs...
    my %levels = ( INFO => 4 );
    

    将内部函数rec2msg从父函数process移到外部。

    你已经从shawn answer得到了理论解释。

    【讨论】:

      猜你喜欢
      • 2014-10-13
      • 2015-01-24
      • 2012-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-11
      相关资源
      最近更新 更多