Подтвердить что ты не робот

Проблема с perl multiline matching

Я пытаюсь использовать perl one-liner для обновления кода, который охватывает несколько строк, и я вижу странное поведение. Вот простой текстовый файл, который показывает проблему, которую я вижу:

ABCD    START
         STOP    EFGH

Я ожидал, что следующее будет работать, но это ничего не заменит:

perl -pi -e 's/START\s+STOP/REPLACE/s' input.txt

После некоторых экспериментов я обнаружил, что \s+ в исходном регулярном выражении будет соответствовать новой строке, но не пробелу во второй строке, а добавление второй \s+ тоже не будет работать. Поэтому на данный момент я делаю следующее обходное решение, которое заключается в добавлении промежуточного регулярного выражения, которое удаляет только новую строку:

perl -pi -e 's/START\s+/START/s' input.txt

Это создает следующий промежуточный файл:

ABCD    START            STOP    EFGH

Затем я могу запустить исходное регулярное выражение (хотя /s больше не требуется):

perl -pi -e 's/START\s+STOP/REPLACE/s' input.txt

Это создает окончательный желаемый файл:

ABCD    REPLACE    EFGH

Кажется, что промежуточный шаг не нужен. Я что-то пропустил?

4b9b3361

Ответ 1

perl -p обрабатывает файл по одной строке за раз. Регулярное выражение у вас правильно, но оно не сопоставляется с многострочной строкой.

Простая стратегия, предполагающая, что файл поместится в памяти, - это прочитать все (сделайте это без -p):

$/ = undef;
$file = <>;
$file =~ s/START\s+STOP/REPLACE/sg;
print $file;

Примечание. Я добавил модификатор /g, чтобы указать глобальную замену.

Как ярлык для всего этого дополнительного шаблона, вы можете использовать существующий script с опцией -0777: perl -0777pi -e 's/START\s+STOP/REPLACE/sg', Добавление /g по-прежнему необходимо, если вам может потребоваться выполнить несколько замен в файле.

Икота, с которой вы можете столкнуться, хотя и не с этим регулярным выражением: если регулярное выражение было START.+STOP, а файл содержит несколько пар START/STOP, жадное сопоставление .+ будет потреблять все, начиная с первого START до Последняя остановка. Вы можете использовать не-жадное соответствие (как можно меньше) с помощью .+?.

Если вы хотите использовать привязки ^ и $ для границ строк в любой точке строки, вам также понадобится модификатор /m regex.

Ответ 2

Ты был близок. Вам нужно либо -00, либо -0777:

 perl -0777 -pi -e 's/START\s+/START/' input.txt

Ответ 3

Относительно простой однострочный (чтение файла в памяти):

perl -pi -e 'BEGIN{undef $/;} s/START\s+STOP/REPLACE/sg;' input.txt

Другая альтернатива (не так просто), не считывая файл в памяти:

perl -ni -e '$a.=$_; \
             if ( $a =~ s/START\s+STOP/REPLACE/s ) { print $a; $a=""; } \
             END{$a && print $a}' input.txt

Ответ 4

Здесь однострочный, который не сразу считывает весь файл в память:

perl -i -ne 'if (($x = $last . $_) =~ s/START\n\s*STOP/REPLACE/) \
  { print $x; $last = ""; } else { print $last; $last = $_; } \
  print $last if eof ARGV' input.txt

Ответ 5

perl -MFile::Slurp -e '$content = read_file(shift); $content =~ s/START\s+STOP/REPLACE/s; print $content' input.txt