Глобальное регулярное выражение соответствует while() по результату backtick

Этот script ищет строки со словами и печатает их, а перечитывает исходный файл на каждой итерации:

# 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

Пожалуйста, объясните мне, почему запуск script не бесконечный.

В любом случае смещение итерации для двигателя regex должно быть reset, потому что выражение изменено (новый cat раздвоен).

Я думал, что perl делает какое-то кэширование для результата cat, но strace утверждает, что кошка была порождена 4 раза (3 для 3 строк + 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';
}

Где разница между двумя сценариями? Что мне не хватает?

Ответы

Ответ 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

Это означает, что единственный способ наблюдения, который наблюдается в примере backticks, - это то, что оператор соответствия сопоставлен с одним и тем же скаляром все четыре раза, когда он был оценен! Как это возможно? Это потому, что backticks является одним из операторов, использующих TARG.

Создание скаляра относительно дорого, так как требует до трех распределений памяти! Для повышения производительности скаляр под названием TARG связан с каждым экземпляром некоторых операторов. Когда оператор с TARG оценивается, он может заполнить TARG значением, возвращаемым и возвращающим TARG (вместо выделения и возврата нового).

"И что?", спросите вы. В конце концов, вы уже продемонстрировали, что назначение скаляру сбрасывает позицию соответствия, связанную с этим скаляром. То, что должно произойти, но это не для обратных сигналов.

Магия не только позволяет привязывать информацию к переменной, но также присоединяет функции, которые будут вызываться при определенных условиях. Магия, добавленная //g, добавляет функцию, которая должна вызываться после изменения скаляра (что указано флагом SMG в дампе выше). Эта функция - это то, что очищает позицию, когда значение присваивается скаляру.

Оператор присваивания правильно обрабатывает магию, но не оператором backticks. Он не ожидает, что магия была добавлена ​​в его TARG, поэтому он не проверяет, есть ли там, поэтому функция, которая очищает позицию соответствия, не имеет значения.