Encontre um padrão e insira # no início de 2 linhas antes disso e 1 linha depois disso

3

Eu tenho um arquivo grande (mais de 2000 linhas). Onde eu tenho que inserir um # no início de 2 linhas acima e no início de 1 linha abaixo depois de encontrar um padrão. Também insira # no início da linha onde o padrão foi encontrado. O ambiente é o Red Hat Linux. Além disso, se você puder explicar, isso seria ótimo.

Tome um exemplo, por favor, veja os textos abaixo, Pesquisa "Fail" e # 2 linhas antes e 1 linha após essa seqüência (início da linha). Também # linha contendo a string "Fail".

Name
Number
Reason = Pass
Reasult
Name
Number
Reason = Pass
Reasult
Name
Number
Reason = Fail
Reasult
Name
Number
Reason = Pass
Reasult
Name
Number
Reason = Fail
Reasult
Name
Number
Reason = Pass
Reasult
    
por Randy 26.08.2015 / 01:31

6 respostas

3

Eu sugiro usar perl :

perl -p0e 's/(.*\n)(.*\n)(.*Fail\n)/####/g' file

Veja como funciona:

  • -p : programa de impressão no loop sobre todas as linhas de entrada
  • -0 : assume null como separador de registro
  • -e : executar programa a partir da linha de comando
  • s/x/y/g : substitua y por x em qualquer lugar no arquivo
  • () : agrupar expressões regulares
  • .* : qualquer caractere, exceto nova linha, repetido zero ou mais vezes
  • \n : newline
  • , , : padrão de acesso do enésimo grupo ()

Saída:

Name
Number
Reason = Pass
Reasult
Name
Number
Reason = Pass
Reasult
#Name
#Number
#Reason = Fail
#Reasult
Name
Number
Reason = Pass
Reasult
#Name
#Number
#Reason = Fail
#Reasult
Name
Number
Reason = Pass
Reasult
    
por 26.08.2015 / 01:56
1

Aqui está uma solução sed usando uma janela deslizante (para que nunca haja mais de quatro linhas no espaço padrão de cada vez):

sed '1{N;N;};$!N;/.*\n.*\n.*Fail.*\n.*/{s/^/#/;s/\n/&#/g;};P;D' infile

Na primeira linha, ele lê as duas linhas N ext (agora há três linhas no espaço padrão). Então, para cada linha de entrada (incluindo a primeira), ela puxa a linha N ext (então agora há quatro linhas no espaço padrão). Se a terceira linha no espaço de padrão corresponder a Fail , ela incluirá cada linha no espaço de padrão com um # . Então, independentemente disso, ele P envia até o primeiro \n ewline e, em seguida, D elimina o primeiro \n ewline, reiniciando o ciclo.

    
por 06.07.2016 / 23:26
0
$ sed -r 'H;1h;$!d;x; s/\n([^\n]*)\n([^\n]*)\n([^\n]*Fail[^\n]*)\n/\n#\n#\n#\n#/g' file
Name
Number
Reason = Pass
Reasult
Name
Number
Reason = Pass
Reasult
#Name
#Number
#Reason = Fail
#Reasult
Name
Number
Reason = Pass
Reasult
#Name
#Number
#Reason = Fail
#Reasult
Name
Number
Reason = Pass
Reasult

Como funciona

  • H;1h;$!d;x

    Estes comandos lêem o arquivo inteiro em.

  • s/\n([^\n]*)\n([^\n]*)\n([^\n]*Fail[^\n]*)\n/\n#\n#\n#\n#/g

    Isso procura por quatro linhas consecutivas com Fail na terceira linha. Se isso for encontrado, então # serão colocados após cada caractere de nova linha.

    Mais detalhadamente, um comando substituto se parece com s/old/new , em que old é uma expressão regular. No nosso caso, é \n([^\n]*)\n([^\n]*)\n([^\n]*Fail[^\n]*)\n . Vamos dividir isso em suas quatro partes:

    1. \n([^\n]*) encontra a primeira linha e a salva no grupo 1.

    2. \n([^\n]*) encontra a segunda linha e a salva no grupo 2.

    3. \n([^\n]*Fail[^\n]*) encontra a terceira linha, mas corresponde apenas se essa linha contiver a palavra Fail .

    4. \n corresponde à quarta nova linha. (O texto da quarta linha não é salvo: não é necessário).

    Se quatro linhas corresponderem ao acima, substituí-las por \n#\n#\n#\n# , que é o mesmo que a entrada, exceto que # são adicionados após cada caractere de nova linha, \n .

    Mac OSX (BSD)

O acima foi testado no GNU sed. Se estiver usando o BSD sed, tente:

sed -E 'H;1h;$!d;x; s/\n([^\n]*)\n([^\n]*)\n([^\n]*Fail[^\n]*)\n/\n#\n#\n#\n#/g' file
    
por 26.08.2015 / 02:52
0

Usando uma "janela" de 4 linhas em Perl:

perl -ne '
    push @w, $_;
    if (4 == @w) {
        if ($w[2] =~ /Fail/) {
            s/^/#/ for @w;
        }
        print @w;
        @w = ();
    }
' < input-file > output-file
  • -n lê a linha de entrada por linha.
  • @w é a janela, acumula linhas até que haja 4 delas. Nesse momento, o terceiro é comparado com /Fail/ e, se presente, cada linha na janela é prefixada com # . Então, a janela é impressa e esvaziada.

Nota: Não é possível imprimir as últimas linhas da entrada se o último bloco for menor que 4 linhas.

    
por 08.07.2016 / 15:59
0

Variante mais simplificada do script @don_crissti

sed ':a;/\(.*\n\)\{2\}/{P;D};N;/= Fail$/! ba;N;s/^/# /gm'
    
por 08.07.2016 / 16:58
0

Apenas por diversão: Este é um loop de shell sobre o editor de linhas interativo ed que resolve o problema:

while ed text.in <script.ed >/dev/null; do
  :  # nothing here
done

O arquivo script.ed contém

/^[^#].*Fail/-2
.,+3s/^/#/
w
  1. A primeira linha do script de edição encontra a próxima linha contendo a palavra Fail que ainda não foi prefixada com um # e recua duas linhas a partir dela.

  2. A segunda linha do script substitui o início da linha por # (ou seja, acrescenta # à linha), pela linha atual e mais três linhas adiante.

  3. A terceira linha do script grava o arquivo de volta no disco.

O loop no shell sairá assim que ed sair com um status de saída diferente de zero. Isso acontecerá quando não for possível encontrar outra linha que corresponda à expressão regular na linha um do script de edição.

Como alternativa, sem um script de edição separado em um arquivo:

while ed text.in >dev/null <<ED_END
/^[^#].*Fail/-2
.,+3s/^/#/
w
ED_END
do
  :  # nothing here
done
    
por 08.07.2016 / 16:53