Como comentar várias linhas correspondentes a um regex usando sed ou awk

3

Eu tenho um arquivo que contém isso:

[...]

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

[...]

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

[...]

E eu quero saber:

[...]

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

[...]

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

[...]

Como posso fazer isso dando meu arquivo para um comando unix?

    
por Natim 07.02.2014 / 18:19

2 respostas

2

Isso não é trivial. Se você puder assumir que cada bloco {} não contém outros blocos {} aninhados, é mais fácil e você pode fazer algo assim:

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

Isso simplesmente define $n a 1 se a linha atual corresponder a location /static e a definir de volta para 0 no primeiro } encontrado depois de location/static . Então, contanto que $n==1 , adicione um # ao início da linha. O -p flag faz com que o perl faça o loop automaticamente pelo arquivo de entrada e imprima cada linha.

Agora, se você pode ter blocos aninhados em profundidade arbitrária dentro dos blocos que deseja comentar, as coisas ficam mais complicadas. Por exemplo, se você tem algo assim:

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

Para casos como esse, a solução simples acima falhará e você terá que usar um que controle o número de { aberto. Por exemplo (este é realmente um one-liner, você pode copiar / colar diretamente no seu terminal, eu apenas o expandi para clareza):

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

Finalmente, se as soluções funcionarem conforme o esperado, você poderá adicionar o -i flag para fazer as alterações no próprio arquivo:

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

Extrair blocos delimitados (possivelmente aninhados) com expressões regulares não é particularmente divertido ou fácil. Existe uma solução elegante, porém, usando um módulo que está sendo enviado com o Perl há muito tempo (já que o Perl era uma de suas tags), viz. Text :: Balanced :

#!/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;
}

Esse script funciona repetidamente consumindo (extraindo) e analisando a parte principal da string, até que nada seja deixado:

  • Se a parte principal contiver a palavra-chave location , seguida de espaços em branco ( \s+ ) e algo que pareça ser um identificador (atualmente identificado muito grosseiramente por ser uma sequência de espaços não brancos caracteres, \S+ ), então extract_bracketed extrairá o bloco delimitado que segue (por padrão, ele extrairá um bloco delimitado por qualquer um dos seguintes pares: [] , {} , () ou <> ). extract_bracketed lidará corretamente com os delimitadores aninhados e balanceados dentro do bloco a ser extraído. A seguinte substituição s/^/# /mg é responsável por comentar as linhas individuais no bloco, independentemente de quantas linhas ele possa conter. O bloco (junto com a palavra-chave de localização principal) é então impresso.

  • Caso contrário, uma linha (até e incluindo o caractere de nova linha) é extraída e impressa sem modificações.

Algumas outras coisas para observar:

  1. o texto é lido e armazenado em sua totalidade em uma string ( $in ) ao indefinir o separador de registro $/
  2. $1 é a variável especial que contém o conteúdo da expressão regular delimitada por parênteses; Por exemplo, para (location\s+\S+\s+) , $ 1 contém o texto location /static (incluindo o espaço à direita).
por 13.02.2014 / 14:35