tsucchi’s diary(元はてなダイアリー)

はてなダイアリー(d.hatena.ne.jp/tsucchi1022)から移行したものです

readpipe(バッククォート演算子)のオーバーロード

バッククォート演算子オーバーロードしたくて、readpipe を上書いてみたのですが、なんか妙な動きをしてくれます。

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

BEGIN {
    *CORE::GLOBAL::readpipe = sub {
        warn Dumper(\@_);
    };
}

`ls -l`;
my $cmd = "ls -l";
`$cmd`;

これを実行すると、両方とも同じ結果になりそうな気がするのですが、実際はなりません。

tsucchi@over[344]% perl a.pl
$VAR1 = [
          'ls -l'
        ];
$VAR1 = [
          '$cmd'
        ];

うーん、これは Perl のバグのような気がするのですが、どうなんでしょうね?

変数展開したくても、再定義された readpipe 側では呼び出し元の変数の情報は(通常は)引っ張れないので、どうにもならない気がします。

何かいい方法あるのかなー?さっきから調べてるのですが、見つかりません。。。

追記

PadWalker 投入ですかね?これはこれで胡散臭いですが。。。peek level をちゃんと決めれるか分かんないですし。。。

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use PadWalker qw(peek_my);

BEGIN {
    *CORE::GLOBAL::readpipe = sub {
        my ($expr) = @_;
        my $walker = peek_my(1);
        my @commands = split(/\s+/, $expr);
        for my $command ( @commands ) {
            $command = ${ $walker->{$command} } if ( $command =~ /^\$/ );
        }
        warn join(' ', @commands);
    };
}

`ls -l`;
my $cmd = "ls -l";
`$cmd`;
tsucchi@over[354]% perl a.pl
ls -l at a.pl line 16.
ls -l at a.pl line 16.

追記2 (2011/07/31)

この問題(?)を解決するために、Variable::Expand::AnyLevelというモジュールを書いてみました

真面目に研究(?)してみてわかったのですが、たぶん peek level は一意に決まる。でも変数展開するためには、手元にいったん変数を全部持ってこないとできないとか、結構めんどくさそうな処理がいろいろあったので、別のモジュールにしてみました。