3

У меня есть файл, который содержит это:

[...]

location /static {
    ...
    multiple lines
    ...
}

[...]

location /static/ {
    ...
    multiple lines
    ...
}

[...]

И я хочу получить:

[...]

# location /static {
#     ...
#     multiple lines
#     ...
# }

[...]

# location /static/ {
#     ...
#     multiple lines
#     ...
# }

[...]

Как мне это сделать, передав мой файл команде unix?

2 ответа2

2

Это не тривиально. Если вы можете предположить, что каждый блок {} не содержит других, вложенных блоков {} это проще, и вы можете сделать что-то вроде этого:

perl -pe 'if(/location\s*\/static/){$n=1}elsif(/}/){$n=0} s/^/#/ if $n==1;' file

Это просто устанавливает $n в 1 если текущая строка соответствует location /static и устанавливает ее обратно в 0 в первом } найденном после location/static . Затем, пока $n==1 , он добавляет знак # к началу строки. Флаг -p заставляет perl автоматически перебирать входной файл и печатать каждую строку.

Теперь, если вы можете иметь произвольные вложенные блоки глубины внутри блоков, которые хотите комментировать, все становится сложнее. Например, если у вас есть что-то вроде этого:

location /static {
   if(foo){
      print "one";
   }
   elsif(bar){
      print "two";
   }
}

В таких случаях простое решение, приведенное выше, не будет работать, и вам придется использовать такое, которое отслеживает количество открытых { . Например (это на самом деле однострочник, вы можете скопировать / вставить прямо в ваш терминал, я просто расширил его для ясности):

perl -pe 'if(/location\s*\/static/){$n=1;}
          elsif(/}/ && $open==0){$n=0} 
          if($n==1 && /{/){$open++} ## count open brackets
          elsif($n==1 && /}/){$open--} ## count closing brackets
          if($n==1 && $open>0){ s/^/#/}; ' file

Наконец, если решения работают должным образом, вы можете добавить флаг -i и внесите изменения в сам файл:

perl -i -pe 'if(/location\s*\/static/){$n=1}elsif(/}/){$n=0} s/^/#/ if $n==1;' file
0

Извлечение (возможно, вложенных) блоков с разделителями с помощью регулярных выражений не особенно весело или просто. Однако есть элегантное решение, использующее модуль, который уже давно поставляется с Perl (поскольку Perl был одним из ваших тегов), а именно. Текст :: Сбалансированный :

#!/usr/bin/env perl
use strict;
use warnings;
use Text::Balanced qw( extract_bracketed );

my $in = do { local $/ = undef; <> };
while( $in ) {
    my $out;
    if    ( $in =~ s/^(location\s+\S+\s+)// ) { ( $out = $1 . extract_bracketed($in) ) =~ s/^/# /mg }
    elsif ( $in =~ s/^(.*[\r\n]*)// )         { $out = $1 }
    print $out;
}

Этот скрипт работает путем многократного потребления (извлечения) и анализа ведущей части строки, пока ничего не останется:

  • Если ведущая часть содержит ключевое слово location , за которым следует пробел (\s+), и что-то, что выглядит как идентификатор, может быть идентификатором (в настоящее время очень грубо идентифицирован как последовательность непробельных символов \S+), тогда extract_bracketed будет извлекать блок с разделителями, который следует (по умолчанию он извлекает блок, разделенный любой из следующих пар: [] , {} , () или <>). extract_bracketed будет корректно работать с вложенными сбалансированными разделителями внутри извлекаемого блока. Следующая подстановка s/^/# /mg отвечает за комментирование отдельных строк в блоке, независимо от того, сколько строк оно может содержать. Блок (вместе с ведущим ключевым словом местоположения) затем распечатывается.

  • В противном случае строка (до и включая символ новой строки) извлекается и печатается без изменений.

Некоторые другие вещи, чтобы отметить:

  1. текст читается и сохраняется полностью в виде строки ($in) путем отмены определения разделителя записей $/
  2. $1 - это специальная переменная, которая содержит содержимое регулярного выражения, разделенного скобками; например, для (location\s+\S+\s+) $ 1 содержит текст location /static (включая завершающий пробел).

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .