Find 2nd Ocorrência de string do final do arquivo

6

Eu tenho um arquivo de log que se parece com isso,

Another thousand lines above this
I 10/03/15 12:04AM 42 [Important] 4th to last
I 10/03/15 04:31AM 42 (534642712) [1974,2106,258605080,0,0,32817,30711]
I 10/03/15 04:33AM 42 (2966927) [91,0,2966927,0,0,291,291]
I 10/03/15 04:52AM 42 (3026559) [93,0,3026559,0,0,314,314]
I 10/03/15 04:55AM 42 (3065494) [94,0,3065494,0,0,301,301]
I 10/03/15 05:04AM 42 [Important] 3rd to last
I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42  (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence

O único padrão que permanece o mesmo em todo este arquivo é [Important] , tudo muda, incluindo o número de linhas entre cada ocorrência de [Important]

Estou tentando pegar o final desse arquivo, ignorar a última ocorrência e encontrar o segundo para o último, depois extrair o conteúdo restante do arquivo para outro.

Isso é o que eu tenho tentado, mas não consegui destacar a segunda até a última ocorrência com tac. O que estou tentando ..

<logfile tac | sed '/Important/q' | tac >  output_file

Isto é o que a saída deve parecer ..

I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42  (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence
    
por Pi4All 04.10.2015 / 20:46

4 respostas

6

Encontre todas as linhas com "Importante", escolha as duas últimas, pegue os números das linhas, imprima o intervalo:

sed -n 'grep -n Important log | tail -n 2 | cut -d : -f 1 | tr '\n' ',' | sed -e 's#,$#p#'' log

Saídas como esperado:

I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42  (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence

Como um script:

#!/bin/bash
lines='grep -n Important log | tail -n 2 | cut -d : -f 1'
range='echo "${lines}" | tr '\n' ',' | sed -e 's#,$#p#''
sed -n "${range}" log
    
por 04.10.2015 / 23:37
5
$ awk '/Important/{pen=s; s=$0;next} s{s=s"\n"$0} END{print pen "\n" s}' logfile
I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42  (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence

Como funciona

o awk implicitamente percorre todas as linhas do arquivo de entrada. Após cada aparição de Important , salvamos as linhas na variável s . Quando chegamos a uma nova linha com Important , o antigo conjunto de linhas importantes é transferido para a variável pen e começamos a salvar as novas linhas em s .

pen tem a penúltima (segunda a última) Important seção. s tem a seção final (último) Important . No final, imprimimos pen e s .

Mais detalhadamente:

  • /Important/{pen=s; s=$0;next}

    Se esta linha contiver Important , mova o conteúdo da variável s para pen , salve a linha atual em s . Então, pule o resto dos comandos e pule para a próxima linha.

  • s{s=s"\n"$0}

    Se chegarmos aqui, a linha atual não contém Important .

    Se s foi definido como um valor, acrescente a linha atual a ele.

  • END{print pen "\n" s}

    Depois que chegarmos ao final do arquivo, imprima pen e s .

por 04.10.2015 / 21:50
3

Se ed for uma opção:

ed -s file <<EOF
1
?Important
?
;w output_file
Q
EOF
    
por 04.10.2015 / 20:53
1

Se sed pode apenas armazenar em buffer todo o arquivo (se você estiver no GNU / qualquer coisa, ele pode),
(última edição: eu consertei vários brainos aqui)

sed -En 'H;$!d
     g;s/.*[\n](.*Important.*\n.*Important[^\n]*).*//p
'    

Os H;$!d buffers ("mantém") cada linha com um \n inicial até o final do arquivo. O que segue o $!d é executado somente após a última linha ter sido armazenada em buffer. g g ets o buffer.

Para entender o regex, lembre-se de que os regex's são mais longos. Um líder. * Encontra a última correspondência para o que segue. Como H adiciona incondicionalmente um \n à frente, o .*\n corresponde a cada linha completa antes de dois "Importantes" serem separados por pelo menos uma nova linha e seguidos por todos os itens até a linha seguinte.

Se não houver duas linhas importantes, nada será impresso.

Pode ser, pelo menos, esteticamente melhor descartar linhas de forma incremental, pois você é indesejado

sed -En 'H
        /Important/    {x; s/.*[\n](.*Important.*\n.*Important[^\n]*)//; H}
        $              {g; s/.*[\n](.*Important.*\n.*Important[^\n]*).*//p }
'

O /Important/ corresponde e x altera o padrão e armazena os buffers, mantém apenas o último bloco interessante e coloca o resultado de volta no buffer de retenção.

Eu coloquei o [\n] entre parênteses apenas para realçá-lo e visualmente combiná-lo com a não-nova classe, uma classe de um caractere pode ser escrita sem os colchetes.

    
por 04.10.2015 / 22:41