该脚本搜索带有单词的行并打印它们,同时在每次迭代中重新读取源文件:

# cat mm.pl
#!/usr/bin/perl
use strict;
use warnings;

while( `cat aa` =~ /(\w+)/g ) {
    print "$1\n";
}


输入文件:

# cat aa
aa
bb
cc


结果:

# ./mm.pl
aa
bb
cc


请向我解释为什么运行脚本不是无止境的。

在每一次迭代中,都应重置正则表达式引擎的偏移量,因为表达式已更改(派生了新的cat)。

我以为perl对cat结果进行某种形式的缓存,但是strace声称cat产生了4次(3行3行+ false条件1行):

# strace -f ./mm.pl 2>&1 | grep cat | grep -v ENOENT
[pid 22604] execve("/bin/cat", ["cat", "aa"], [/* 24 vars */] <unfinished ...>
[pid 22605] execve("/bin/cat", ["cat", "aa"], [/* 24 vars */] <unfinished ...>
[pid 22606] execve("/bin/cat", ["cat", "aa"], [/* 24 vars */] <unfinished ...>
[pid 22607] execve("/bin/cat", ["cat", "aa"], [/* 24 vars */] <unfinished ...>


另一方面,以下示例将永远运行:

# cat kk.pl
#!/usr/bin/perl
use strict;
use warnings;

my $d = 'aaa';
while( $d =~ /(\w+)/g ) {
    print "$1\n";
    $d = 'aaa';
}


这两个脚本之间有何区别?我想念什么?

最佳答案

//g离开的位置存储在添加到执行匹配的标量的魔术中。

$ perl -MDevel::Peek -e'$_ = "abc"; Dump($_); /./g; Dump($_);'
SV = PV(0x32169a0) at 0x3253ee0
  REFCNT = 1
  FLAGS = (POK,IsCOW,pPOK)
  PV = 0x323bae0 "abc"\0
  CUR = 3
  LEN = 10
  COW_REFCNT = 1
SV = PVMG(0x326c040) at 0x3253ee0
  REFCNT = 1
  FLAGS = (SMG,POK,IsCOW,pPOK)
  IV = 0
  NV = 0
  PV = 0x323bae0 "abc"\0
  CUR = 3
  LEN = 10
  COW_REFCNT = 2
  MAGIC = 0x323d050
    MG_VIRTUAL = &PL_vtbl_mglob
    MG_TYPE = PERL_MAGIC_regex_global(g)
    MG_FLAGS = 0x40
      BYTES
    MG_LEN = 1


这意味着在反引号示例中观察到行为的唯一可能方式是,如果match运算符对同一个标量进行了四次匹配!那怎么可能?这是因为反引号是使用TARG的运营商之一。

创建标量相对昂贵,因为它最多需要三个内存分配!为了提高性能,一个称为TARG的标量与某些运算符的每个实例相关联。当对具有TARG的运算符进行评估时,可能会在TARG中填充要返回并返回TARG的值(而不是分配并返回新值)。

您可能会问:“那又怎样?”毕竟,您已经证明了分配给标量会重置与该标量关联的匹配位置。那是应该发生的事情,但不是反引号。

Magic不仅允许将信息附加到变量,而且还附加了在某些条件下要调用的函数。 //g添加的魔术附加了在修改标量后应调用的函数(由上面的转储中的SMG标志指示)。该功能是在将值分配给标量后清除位置的功能。

赋值运算符可以正确处理魔术,但反引号运算符不能正确处理。它不希望将魔术添加到其TARG中,因此它不会检查是否存在魔术,因此清除匹配位置的函数不会被调用。

10-02 04:44