【问题标题】:Global regex match in while() on backtick resultwhile() 中的全局正则表达式匹配反引号结果
【发布时间】:2017-04-06 13:49:45
【问题描述】:

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

# 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

请解释一下为什么运行脚本不是无止境

在正则表达式引擎的每个 while 迭代偏移中应重置,因为表达式已更改(新的 cat 已分叉)。

我认为 perl 对 cat 结果进行了某种缓存,但 strace 声称 cat 被生成了 4 次(3 行 3 行 + 1 错误 while 条件) :

# 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';
}

这两个脚本的区别在哪里?我错过了什么?

【问题讨论】:

  • 这是个好问题
  • 对于那些没有 strace 的用户,您可以使用例如这个 bash 脚本 mycat 作为 for i in aa bb cc; do echo "${i}_$BASHPID"; done (ofc chmod 755 mycat)。它会为每个循环显示不同的$BASH_PID。 :)
  • 也许反引号正在重用相同的内存缓冲区,这使 /g 误以为它是同一个字符串。
  • qx 反击。第 V 集 - 为什么不使用反引号... :)

标签: regex perl


【解决方案1】:

//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

这意味着在反引号示例中观察到的行为可能的唯一方法是,如果匹配运算符在它被评估的所有四次都与相同的标量匹配!这怎么可能?这是因为反引号是使用 TARG 的运算符之一。

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

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

Magic 不仅允许将信息附加到变量上,还可以附加要在特定条件下调用的函数。 //g 添加的魔法附加了一个应在修改标量后调用的函数(由上面转储中的 SMG 标志指示)。此函数用于在将值分配给标量时清除位置。

赋值运算符正确处理魔法,但不是由反引号运算符。它并不期望魔法被添加到它的 TARG 中,所以它不检查是否有任何魔法,所以清除匹配位置的函数未被调用。

【讨论】:

  • 听起来像是要为我报告的错误。
  • @Oleg V. Volkov,这是一个错误,但我不知道修复它有什么好处。我无法弄清楚它会造成伤害的情况,它实际上在 OP 的案例中有所帮助
  • @ikegami 虽然有帮助,但也令人困惑。无限循环似乎是天真的期望。
  • @Micha Wiedenmann,不要误会我的意思,人们不应该依赖错误的行为。我只是说如果他们不小心这样做了,他们的程序仍然有效,这不是一件坏事。
猜你喜欢
  • 2011-04-05
  • 1970-01-01
  • 1970-01-01
  • 2016-12-13
  • 2013-11-23
  • 2020-10-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多