如何重新定义内置 Perl 函数?

发布于 2024-07-14 22:52:08 字数 387 浏览 7 评论 0原文

我想做两件事:

在生产代码中,我想重新定义 open 命令以使我能够添加自动文件日志记录。 我致力于数据处理应用程序/流程,作为其中的一部分,用户准确了解正在处理哪些文件非常重要。 如果他们使用的是旧版本的文件,他们找出问题的一种方法是阅读正在处理的文件列表。

我可以创建一个新的 sub 来执行此日志记录并返回一个文件指针,并在我的代码中使用它来代替 open 。

如果我可以重新定义 open 并使预先存在的代码从这种行为中受益,那就太好了。 我可以这样做吗?

在调试代码中,我想重新定义 printf 命令以插入注释以及指示哪个代码生成该行的书面输出。 同样,我有一个可以选择执行此操作,但转换我现有的代码很乏味。

I want to do two things:

In production code, I want to redefine the open command to enable me to add automagic file logging. I work on data processing applications/flows and as part of that, it's important for the user to know exactly what files are being processed. If they are using an old version of a file, one way for them to find out is by reading through the list of files being processed.

I could just create a new sub that does this logging and returns a file pointer and use that in place of open in my code.

It would be really nice if I could just redefine open and have pre-existing code benefit from this behavior. Can I do this?

In debug code, I'd like to redefine the printf command to insert comments along with the written output indicating which code generated that line. Again, I have a sub that will optionally do this, but converting my existing code is tedious.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

魄砕の薆 2024-07-21 22:52:08

如果 CORE 子例程有原型*,则可以替换它。 替换当前命名空间中的函数非常简单。

#!/usr/bin/perl

use strict;
use warnings;

use subs 'chdir';

sub chdir(;$) {
    my $dir = shift;
    $dir    = $ENV{HOME} unless defined $dir;
    print "changing dir to $dir\n";
    CORE::chdir $dir;
}

chdir("/tmp");
chdir;

如果您还想覆盖所有模块的函数,您可以阅读文档。

* 下面是测试 Perl 5.10 中每个函数的代码(它也适用于早期版本)。 请注意,某些函数可以被覆盖,该程序会告诉您不能,但被覆盖的函数的行为方式将与原始函数不同。

来自 perldoc -f 原型

如果内置函数不可重写
(例如 qw//)或者如果它的参数
无法充分表达
原型(例如系统),
prototype() 返回 undef,因为
内置的行为并不像
Perl函数

#!/usr/bin/perl

use strict;
use warnings;

for my $func (map { split } <DATA>) {
    my $proto;
    #skip functions not in this version of Perl
    next unless eval { $proto = prototype "CORE::$func"; 1 };
    if ($proto) {
        print "$func has a prototype of $proto\n";
    } else {
        print "$func cannot be overridden\n";
    }
}

__DATA__
abs          accept         alarm          atan2            bind          
binmode      bless          break          caller           chdir
chmod        chomp          chop           chown            chr
chroot       close          closedir       connect          continue
cos          crypt          dbmclose       defined          delete
die          do             dump           each             endgrent 
endhostent   endnetent      endprotoent    endpwent         endservent
eof          eval           exec           exists           exit
exp          fcntl          fileno         flock            fork
format       formline       getc           getgrent         getgrgid
getgrnam     gethostbyaddr  gethostbyname  gethostent       getlogin
getnetbyaddr getnetbyhost   getnetent      getpeername      getpgrp
getppid      getpriority    getprotobyname getprotobynumber getprotoent
getpwent     getpwnam       getpwuid       getservbyname    getservbyport
getservent   getsockname    getsockopt     glob             gmtime
goto         grep           hex            import           index
int          ioctl          join           keys             kill
last         lc             lcfirst        length           link
listen       local          localtime      lock             log
lstat        m              map            mkdir            msgctl
msgget       msgrcv         msgsnd         my               next
no           oct            open           opendir          ord
our          pack           package        pipe             pop
pos          print          printf         prototype        push
q            qq             qr             quotemeta        qw
qx           rand           read           readdir          readline
readlink     readpipe       recv           redo             ref
rename       require        reset          return           reverse
rewinddir    rindex         rmdir          s                say
scalar       seek           seekdir        select           semctl
semget       semop          send           setgrent         sethostent
setnetent    setpgrp        setpriority    setprotoent      setpwent
setservent   setsockopt     shift          shmctl           shmget
shmread      shmwrite       shutdown       sin              sleep
socket       socketpair     sort           splice           split
sprintf      sqrt           srand          stat             state
study        sub            substr         symlink          syscall
sysopen      sysread        sysseek        system           syswrite
tell         telldir        tie            tied             time
times        tr             truncate       uc               ucfirst
umask        undef          unlink         unpack           unshift
untie        use            utime          values           vec
wait         waitpid        wantarray      warn             write
y            -r             -w             -x               -o
-R           -W             -X             -O               -e
-z           -s             -f             -d               -l
-p           -S             -b             -c               -t
-u           -g             -k             -T               -B
-M           -A             -C

If a CORE subroutine has a prototype* it can be replaced. Replacing a function in the current namespace is simple enough.

#!/usr/bin/perl

use strict;
use warnings;

use subs 'chdir';

sub chdir(;$) {
    my $dir = shift;
    $dir    = $ENV{HOME} unless defined $dir;
    print "changing dir to $dir\n";
    CORE::chdir $dir;
}

chdir("/tmp");
chdir;

If you want to override the function for all modules as well you can read the docs.

* Here is code to test every function in Perl 5.10 (it will work on earlier versions as well). Note, some functions can be overridden that this program will tell you can't be, but the overridden function will not behave in the same way as the original function.

from perldoc -f prototype

If the builtin is not overridable
(such as qw//) or if its arguments
cannot be adequately expressed by a
prototype (such as system),
prototype() returns undef, because the
builtin does not really behave like a
Perl function

#!/usr/bin/perl

use strict;
use warnings;

for my $func (map { split } <DATA>) {
    my $proto;
    #skip functions not in this version of Perl
    next unless eval { $proto = prototype "CORE::$func"; 1 };
    if ($proto) {
        print "$func has a prototype of $proto\n";
    } else {
        print "$func cannot be overridden\n";
    }
}

__DATA__
abs          accept         alarm          atan2            bind          
binmode      bless          break          caller           chdir
chmod        chomp          chop           chown            chr
chroot       close          closedir       connect          continue
cos          crypt          dbmclose       defined          delete
die          do             dump           each             endgrent 
endhostent   endnetent      endprotoent    endpwent         endservent
eof          eval           exec           exists           exit
exp          fcntl          fileno         flock            fork
format       formline       getc           getgrent         getgrgid
getgrnam     gethostbyaddr  gethostbyname  gethostent       getlogin
getnetbyaddr getnetbyhost   getnetent      getpeername      getpgrp
getppid      getpriority    getprotobyname getprotobynumber getprotoent
getpwent     getpwnam       getpwuid       getservbyname    getservbyport
getservent   getsockname    getsockopt     glob             gmtime
goto         grep           hex            import           index
int          ioctl          join           keys             kill
last         lc             lcfirst        length           link
listen       local          localtime      lock             log
lstat        m              map            mkdir            msgctl
msgget       msgrcv         msgsnd         my               next
no           oct            open           opendir          ord
our          pack           package        pipe             pop
pos          print          printf         prototype        push
q            qq             qr             quotemeta        qw
qx           rand           read           readdir          readline
readlink     readpipe       recv           redo             ref
rename       require        reset          return           reverse
rewinddir    rindex         rmdir          s                say
scalar       seek           seekdir        select           semctl
semget       semop          send           setgrent         sethostent
setnetent    setpgrp        setpriority    setprotoent      setpwent
setservent   setsockopt     shift          shmctl           shmget
shmread      shmwrite       shutdown       sin              sleep
socket       socketpair     sort           splice           split
sprintf      sqrt           srand          stat             state
study        sub            substr         symlink          syscall
sysopen      sysread        sysseek        system           syswrite
tell         telldir        tie            tied             time
times        tr             truncate       uc               ucfirst
umask        undef          unlink         unpack           unshift
untie        use            utime          values           vec
wait         waitpid        wantarray      warn             write
y            -r             -w             -x               -o
-R           -W             -X             -O               -e
-z           -s             -f             -d               -l
-p           -S             -b             -c               -t
-u           -g             -k             -T               -B
-M           -A             -C
稀香 2024-07-21 22:52:08

对于开放:这对我有用。

use 5.010;
use strict;
use warnings;
use subs 'open';
use Symbol qw<geniosym>;

sub open (*$;@) { 
    say "Opening $_[-1]";
    my ( $symb_arg ) = @_;
    my $symb;
    if ( defined $symb_arg ) { 
        no strict;
        my $caller = caller();
        $symb = \*{$symb_arg};
    }
    else { 
        $_[0] = geniosym;
    }
    given ( scalar @_ ) { 
        when ( 2 ) { return CORE::open( $symb // $_[0], $_[1] ); }
        when ( 3 ) { return CORE::open( $symb // $_[0], $_[1], $_[2] ); }
    }
    return $symb;
}

open PERL4_FH, '<', 'D:\temp\TMP24FB.sql';
open my $lex_fh, '<', 'D:\temp\TMP24FB.sql';

对于Printf:您检查过这个问题吗? -> 如何挂钩 Perl 的打印?

For open: This worked for me.

use 5.010;
use strict;
use warnings;
use subs 'open';
use Symbol qw<geniosym>;

sub open (*$;@) { 
    say "Opening $_[-1]";
    my ( $symb_arg ) = @_;
    my $symb;
    if ( defined $symb_arg ) { 
        no strict;
        my $caller = caller();
        $symb = \*{$symb_arg};
    }
    else { 
        $_[0] = geniosym;
    }
    given ( scalar @_ ) { 
        when ( 2 ) { return CORE::open( $symb // $_[0], $_[1] ); }
        when ( 3 ) { return CORE::open( $symb // $_[0], $_[1], $_[2] ); }
    }
    return $symb;
}

open PERL4_FH, '<', 'D:\temp\TMP24FB.sql';
open my $lex_fh, '<', 'D:\temp\TMP24FB.sql';

For Printf: Did you check out this question? -> How can I hook into Perl’s print?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文