你能钩住DATA手柄的开口吗?

时间:2012-03-05 22:17:58

标签: perl perl-module

在Perl还在编译时,你能挂钩模块的DATA句柄的开口吗?我的意思是,有一种方法可以插入代码,在 Perl打开DATA glob进行读取之后运行但是之前编译阶段已经停止。

如果失败了,你能在编译器打开它之前至少看到__DATA__之后的 raw 文本吗?


为了回应Ikegami,在我最近编写的最新脚本上,我一直使用__DATA__ section + YAML语法来配置脚本。我也在构建YAML配置处理程序的词汇表,其中use - 模块请求行为。在一些快速脏,但不足以放弃strict的脚本中,我想看看是否可以公开YAML规范中的变量。

虽然只是在import潜艇中保存数据然后等待INIT块来处理YAML,但这有点令人讨厌。但这是可行的。

6 个答案:

答案 0 :(得分:3)

DATA中的文件句柄就是解析器用来读取__DATA__之前找到的代码的句柄。如果该代码仍在编译中,那么__DATA__尚未到达,则句柄尚未存储在DATA中。

您可以执行以下操作:

open(my $data_fh, '<', \<<'__EOI__');
.
. Hunk of text readable via $data_fh
.
__EOI__

答案 1 :(得分:2)

我不知道你想要的钩子。可能在UNITCHECK

use warnings;

sub i'm {
    print "in @_\n";
    print scalar <DATA>;
}

BEGIN       { i'm "BEGIN" }
UNITCHECK   { i'm "UNITCHECK" }
CHECK       { i'm "CHECK" }
INIT        { i'm "INIT" }
END         { i'm "END" }

i'm "main";
exit;

__END__
Data line one.
Data line two.
Data line three.
Data line four.
Data line five.
Data line six.

运行时生成:

in BEGIN
readline() on unopened filehandle DATA at /tmp/d line 5.
in UNITCHECK
Data line one.
in CHECK
Data line two.
in INIT
Data line three.
in main
Data line four.
in END
Data line five.

答案 2 :(得分:2)

您可以使用任何运行时之前但在编译块之后更改*DATA句柄。以下是使用INIT*DATA更改为uc的简短示例。

while (<DATA>) {
    print;
}

INIT {  # after compile time, so DATA is opened, but before runtime.
    local $/;
    my $file = uc <DATA>;
    open *DATA, '<', \$file;
}

__DATA__
hello,
world!

打印:

HELLO,
WORLD!

使用哪个块取决于程序中的其他因素。有关各种定时块的更多详细信息,请参见perlmod联机帮助页。

答案 3 :(得分:1)

如果我的问题正确,我恐怕不会。它写在The Doc

  

请注意,您无法从BEGIN块中的DATA文件句柄中读取:   BEGIN块一看到就会被执行(期间)   汇编),此时相应的 DATA (或 END )   令牌尚未见过。

还有另一种方法:用 DATA 部分作为普通文本文件读取文件,解析此部分,然后require脚本文件本身(将在运行时完成 - 时间)。不知道它是否与你的情况有关。 )

答案 4 :(得分:1)

perlmod说:

  

CHECK代码块在初始Perl编译阶段结束后和运行时间开始之前以LIFO顺序运行。

你可能正在寻找这样的东西吗?

CHECK {
    say "Reading from <DATA> ...";
    while (<DATA>) {
        print;
        $main::count++;
    };
}

say "Read $main::count lines from <DATA>";

__DATA__
1
2
3
4
5

这会产生以下输出:

Reading from <DATA> ...
1
2
3
4
5
Read 5 lines from <DATA>

答案 5 :(得分:0)

我发现::STDIN实际上允许我访问流'-'。我完成后,我可以通过tell ( $inh )然后seek ()保存当前位置。

通过使用该方法,我可以阅读__DATA__ sub中的import部分!

sub import { 
    my ( $caller, $file ) = ( caller 0 )[0,1];
    my $yaml;
    if ( $file eq '-' ) {
        my $place = tell( ::STDIN );
        local $RS;
        $yaml  = <::STDIN>;
        seek( ::STDIN, $place, 0 );
    }
    else { 
        open( my $inh, '<', $file );
        local $_ = '';
        while ( defined() and !m/^__DATA__$/ ) { $_ = <$inh>; }
        local $RS;
        $yaml = <$inh>;
        close $inh;
    }        
    if ( $yaml ) { 
        my ( $config ) = YAML::XS::Load( $yaml );;
        no strict 'refs';
        while ( my ( $n, $v ) = each %$config ) { 
            *{"$caller\::$n"} = ref $v ? $v : \$v;
        }
    }    
    return;
}

这适用于Strawberry Perl 5.16.2,所以我不知道这是多么便携。但是现在,对我而言,这是有效的。

只是一个背景。我曾经用Windows脚本文件做一些编程。我喜欢wsf格式的一件事是你可以在代码之外指定全局有用的对象。 <object id="xl" progid="Application.Excel" />。我一直喜欢按规范编程的外观,让一些模块化处理程序对数据进行排序。现在我可以通过YAML处理程序获得类似的行为:excel: !ActiveX: Excel.Application

这适合我。

如果你有兴趣,测试就在这里:

use strict;
use warnings;
use English qw<$RS>;
use Test::More;

use data_mayhem; # <-- that's my module.

is( $k, 'Excel.Application' );
is( $l[1], 'two' );

{   local $RS;
    my $data = <DATA>;
    isnt( $data, '' );
    say $data
}

done_testing;

__DATA__
---
k : !ActiveX Excel.Application
l : 
  - one
  - two
  - three