sed - como (não) corresponder aos parênteses sem correspondência

4

Como posso fazer minha operação apenas em linhas que fecham seus colchetes?

Por exemplo, quero alterar as linhas ".is" e adicionar um suporte, mas não para linhas com colchetes não fechados.

então estes são alterados

this_thing.is 24  ->   (this_thing).is 24
that.is 50        ->   (that).is 50
a[23].is == 10    ->     a[23].is == 10
a.is true         ->   (this_thing).is 24
this_thing.is 24  ->   (this_thing).is 24

mas estes não são:

this_thing.is (24
((that).is 50
(a[23].is == 10
a.is ( true
(this_thing.is 24

idealmente também (não)

a{.is true
this_thing{.is 24

e (não)

a[.is true
this_thing[.is 24 

Eu tenho um matcher com /.is/ mas como combinar colchetes sem correspondência?

    
por Michael Durrant 15.11.2013 / 03:34

3 respostas

5

Pessoalmente, se minhas expressões regulares estivessem se aproximando desse nível de complexidade, eu mudaria toda a operação para Perl. Este lida com um número arbitrário de chaves abertas / parênteses / chaves:

$ perl -ne '@open=/[\[({]/g; @close=/[)\]}]/g; 
             if($#close == $#open){s/(.+?)\.is/($1).is/} print' file

Ou mais compacto:

$ perl -pne 's/(.+?)\.is/($1).is/ if $#{/[\[({]/g} == $#{/[)\]}]/g}' file

Ou, mais completo, este pode lidar com casos como [} (mas ainda falha em casos como )( ):

  $ perl -pne '@osqb=/\[/g; @csqb=/\]/g; 
               @ocb=/\{/g; @ccb=/\}/g; 
               @op=/\(/g; @cp=/\)/g;
               if($#osqb == $#csqb && $#ocb==$#ccb && $#op == $#cp){
                    s/(.+?)\.is/($1).is/
               }' file

Quando executado no seu exemplo, isso será impresso

(this_thing).is 24
(that).is 50      
(a[23]).is == 10 
(a).is true      
(this_thing).is 24
this_thing.is (24
((that).is 50
(a[23].is == 10
a.is ( true
(this_thing.is 24
a{.is true
this_thing{.is 24
a[.is true
this_thing[.is 24 

Explicação

  • perl -ne : processa o arquivo de entrada linha a linha ( -n ) e executa o script fornecido por -e .
  • @open=/[\[({]/g; : encontre todos os glifos de abertura e salve o resultado em uma matriz chamada @open .
  • @close=/[)\]}]/g; : como acima, mas para o fechamento de glifos.
  • if($#close == $#open) : se o número de glifos de abertura for igual ao número de glifos de fechamento (se, em outras palavras, houver parênteses pendentes, etc.) ...
  • s/(.+?)\.is/($1).is/ : ... então, substitua a cadeia mais curta que termina em .is por si mesma entre parênteses.
  • O último print está fora dos colchetes e será executado se houve uma substituição ou não.
por 15.11.2013 / 05:41
1

Sed usa regex, expressões regulares não são poderosas o suficiente para isso. Use o gawk ou alguma outra ferramenta que possa fazer isso.

Existe uma classificação das gramáticas que mostram isso: regular, sem contexto, etc. Os parênteses correspondentes não podem ser feitos com uma linguagem regular. Então você não pode fazê-lo de forma confiável.

    
por 15.11.2013 / 10:00
1

Expandindo a resposta do Terdon, você pode usar o Perl para realmente analisar construções entre parênteses aninhadas. Aqui está uma regex que deve fazer isso:

$balanced_parens_grammar = qr/
  (?(DEFINE)                         # Define a grammar
    (?<BALANCED_PARENS> 
       \(                            # Opening paren
          (?:                        # Group without capturing
              (?&BALANCED_PARENS)    # Nested balanced parens
             |(?&BALANCED_BRACKETS)  # Nested balanced brackets
             |(?&BALANCED_CURLIES)   # Nested balanced curlies
             |[^)]*                  # Any non-closing paren
           )                         # End alternation
        \)                           # Closing paren
    )
    (?<BALANCED_BRACKETS> 
       \[                            # Opening bracket
          (?:                        # Group without capturing
              (?&BALANCED_PARENS)    # Nested balanced parens
             |(?&BALANCED_BRACKETS)  # Nested balanced brackets
             |(?&BALANCED_CURLIES)   # Nested balanced curlies
             |[^\]]*                 # Any non-closing bracket
           )                         # End alternation
        \]                           # Closing bracket
    )
    (?<BALANCED_CURLIES> 
       {                             # Opening curly
          (?:                        # Group without capturing
              (?&BALANCED_PARENS)    # Nested balanced parens
             |(?&BALANCED_BRACKETS)  # Nested balanced brackets
             |(?&BALANCED_CURLIES)   # Nested balanced curlies
             |[^}]*                  # Any non-closing curly
           )                         # End alternation
        }                            # Closing curly
    )
  )
  (?<BALANCED_ANY>
     (?:
         (?&BALANCED_PARENS)    
        |(?&BALANCED_BRACKETS)  
        |(?&BALANCED_CURLIES)   
     )
  )
/x;

Use assim:

if( $line =~ m/
        ^
          [^()\[\]{}]*       # Any non-parenthetical punctuation
          (?&BALANCED_ANY)?  # Any balanced paren-types
          [^()\[\]{}]*
        $
        $balanced_parens_grammar/x){
    # Do your magic here
}

Aviso de isenção

O código é completamente não testado. Provavelmente contém erros.

    
por 17.11.2013 / 00:06