Como remover um grupo de linhas de um arquivo?

0

Estou escrevendo um script ksh para analisar um arquivo de log e enviar um email quando mensagens importantes são encontradas. Algumas mensagens são informativas e Eu gostaria de ignorá-los.

O arquivo de log tem formato

2018-01-24.08.24.35.875675    some text

    more text
    more text
    more text
    more text

2018-01-24.08.24.37.164538    some text

    more text
    more text
    INF9999W        <-- informational text
    more text

2018-01-24.08.24.46.8602545    some text

    more text
    more text
    more text

O timestamp seria considerado o separador de mensagens, com o timestamp pertencente à mensagem que o segue. Eu gostaria de pesquisar o arquivo para cada ocorrência de "texto informativo" e depois remova toda a mensagem do arquivo (do registro de data e hora anterior) até pouco antes do próximo timestamp).

Como posso determinar facilmente os números de linha do precedente e seguintes timestamps, então eu removo essas linhas com:

awk 'NR<'$preceding_ts' || NR >='$following_ts'

Minha maneira é obter todas as linhas de timestamp em um arquivo e, em seguida, fazer um loop através desse arquivo até encontrar as linhas de timestamp que são apenas antes e depois da linha de "texto de informação". Parece um muito trabalho, particularmente em um arquivo grande. Existe um mais eficiente maneira.

integer inf_line
integer last_ts_line
integer cur_ts
cp $error_log $copy_log
while true
do
   inf_line=$(grep -n "INF99999W" $copy_log | head -1 | cut -f1 -d":")
   if [[ $inf_line -eq 0 ]]
   then
      break
   fi     
   grep -n -E "^20[0-9][0-9]-[0-1][0-9]-[0-3][0-9]-" $copy_log | cut -f1 -d":" > $ts_lines
   last_ts_line=99999999
   cat $ts_lines | while read cur_ts
   do       
      if [[ $cur_ts -gt $inf_line && $last_ts_line -lt $inf_line ]]
      then
         awk 'NR<'$last_ts_line' || NR >='$cur_ts'' $copy_log > $temp_log
         cp $temp_log $copy_log
         last_ts_line=$cur_ts
         break
      fi
      last_ts_line=$cur_ts
   done
   if [[ $last_ts_line -lt $inf_line ]]
   then
      awk 'NR<'$last_ts_line'' $copy_log > $temp_log
      cp $temp_log $copy_log
   fi
done

Obrigado.

    
por Scavenger 24.01.2018 / 18:07

1 resposta

2

Eu abordaria isso armazenando as linhas da mensagem atual e, quando a mensagem terminar, imprimindo o lote armazenado, se não houver nenhum marcador INF visto. Aqui, d contém as linhas da mensagem atual (d para dados), p informa se queremos imprimir as linhas armazenadas ou não.

awk -vinfo='INF99+' \
    '/^20[0-9][0-9]-[0-1][0-9]-[0-3][0-9]/ {
         if (p) printf "%s", d; d = $0 ORS; p=1; next } 
     $0 ~ info {p=0} 
     {d = d $0 ORS} 
     END {if (p) printf "%s", d}' < log 

A primeira regra aqui corresponde às linhas de timestamp, imprime todas as linhas armazenadas se p for true, armazena essa linha e define p para uma. A segunda regra redefine p para zero se uma linha com o padrão info for vista; o padrão é definido para a variável com -vinfo=... . A terceira regra anexa a linha atual às coletadas, e a regra END apenas imprime as linhas coletadas se p estiver definido.

Também poderíamos escrevê-lo assim, isso verificaria o padrão info também na linha de timestamp:

awk -vinfo='INF99+' \
    '/^20[0-9][0-9]-[0-1][0-9]-[0-3][0-9]/ {
         if (p) { printf "%s", d }; d = ""; p=1; } 
     $0 ~ info {p=0} 
     {d = d $0 ORS} 
     END {if (p) printf "%s", d}' < log 

Em geral, é provavelmente uma boa ideia escrever coisas como esta em awk ou Perl. O resultado será, no mínimo, muito mais rápido de executar do que um script de shell que bifurca dezenas de cópias de grep , awk e cut etc ...

    
por 24.01.2018 / 18:21