Linux - Identifica um multi-padrão em um conjunto de linhas e substitui

2

Conteúdo da entrada:

objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: org-abc
objectClass: org-xyz

objectClass: top
objectClass: inetOrgPerson
objectClass: org-abc
objectClass: organizationalPerson
objectClass: person

objectClass: top
objectClass: org-abc
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: org-xyz

objectClass: top
objectClass: inetOrgPerson
objectClass: org-xyz
objectClass: organizationalPerson
objectClass: person

Leia um arquivo LDIF de 50 MB.

O conteúdo entre duas novas linhas é considerado um BLOCO .

  • se ambas as linhas (objectClass: org-abc & objectClass: org-xyz) estiverem presentes em qualquer ordem em um BLOCK , remova essas 2 linhas em um BLOCO e adicione uma nova linha como "objectClass: org-111 "

(OR)

  • se esta linha "objectClass: org-abc" estiver presente em um BLOCK , substitua essa linha por "objectClass: org-222"

(OR)

  • se esta linha "objectClass: org-xyz" sozinha estiver presente em um BLOCK , então substitua essa linha por "objectClass: org-333"

Resultado esperado:

objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: org-111

objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: org-222

objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: org-111

objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: org-333

Como posso obter essa saída usando comandos do Linux (sed ou awk) ou sugerir uma abordagem melhor?

    
por Mahe 16.11.2017 / 09:26

3 respostas

2

Solução AWK complexa:

awk 'function process(a,c) {       # process the lines of one passed block
         for (i=1; i<=c; i++) {    
             split(a[i], fields);  # split the line into 2 fields
             if (fields[2]=="org-abc") abc="222"; 
             else if (fields[2]=="org-xyz") xyz="333"; 
             else print a[i] 
         } 
         if (abc || xyz) printf "objectClass: org-%s\n",(abc && xyz? "111" : (abc? "222":"333")) 
     }
     !NF{ process(a, c); c=abc=xyz=0 }
     { a[++c]=$0 }
     END{ process(a, c) }' file

Esta é a solução Memória suficiente , porque a matriz a manterá as linhas de um único bloco somente durante todo o tempo de processamento . (o contador c está sendo resetado em cada próximo bloco )

A saída:

objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: org-111

objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: org-222

objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: org-111

objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: org-333
    
por 16.11.2017 / 10:24
0

Este é um caso de uso típico do "modo de parágrafo" de Perl ( -00 ), onde "linhas" são definidas por \n\n , então cada parágrafo é tratado como uma única linha:

$ perl -00 -lpe 'if(/: org-abc/ && /: org-xyz/){
                    s/(^|\n)[^\n]+: (org-abc|org-xyz)\s*(?=$|\n)//g;
                    s/$/\nobjectClass: org-111/;
                 }
                 else{
                    s/objectClass: org-abc/objectClass: org-222/; 
                    s/objectClass: org-xyz/objectClass: org-333/
                 }' file
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: org-111

objectClass: top
objectClass: inetOrgPerson
objectClass: org-222
objectClass: organizationalPerson
objectClass: person

objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: org-111

objectClass: top
objectClass: inetOrgPerson
objectClass: org-333
objectClass: organizationalPerson
objectClass: person

Aqui está a mesma coisa sem sentido em um roteiro para maior clareza:

#!/usr/bin/env perl

## Paragraph mode
local $/="\n\n";

my $pat1 = 'objectClass: org-abc';
my $pat2 = 'objectClass: org-xyz';

## Read input file
while (my $line = <>) {
  ## Remove trailing newlines
  chomp($line);

  if($line =~ /$pat1/ && $line=~ /$pat2/){
    $line =~ s/(^|\n)($pat1|pat2)\s*(?=$|\n)//g;
    $line =~ s/$/\nobjectClass: org-111/;
  }
  else{
    $line =~ s/$pat1/objectClass: org-222/;
    $line =~ s/$pat2/objectClass: org-333/
  }
  print "$line\n\n";
}
    
por 16.11.2017 / 11:15
0

Também é fácil com sed :

sed '/^$/!{H;1h;$!d;};x
  /objectClass: org-abc/!{s/\(objectClass: org-\)xyz/33/;p;d;}
  s/\(objectClass: org-\)xyz/11/;t1
  s/\(objectClass: org-\)abc/22/;:b
  :1
  s/\nobjectClass: org-abc//'

A primeira linha é coletar um bloco no espaço padrão, o resto faz as substituições óbvias.

    
por 16.11.2017 / 13:32