Ajuda-me com o meu comando (principalmente awk)

2

Acabei de escrever o comando mais contorcido que já escrevi e quero saber como posso melhorar.

Eu escrevi isto:

grep -E '00[7-9]\.|0[1-9][0-9]\.' filename.log | awk '{print $6}' | sed 's/\(.*\):.*//' | sort | uniq -c | sort -rn

Um exemplo de entrada:

2011/06/30 07:59:43:81 20626 code_file.c (252): FunctionName: 009.63 seconds

Basicamente, o que está fazendo é passar por um arquivo de log que lista o número de segundos que levou um comando para executar e pegar qualquer um deles que levou entre 7 e 99 segundos para ser executado. Então o awk está imprimindo a sexta palavra, que é o nome da função seguido por dois pontos. Então sed está removendo o cólon e qualquer espaço em branco à direita, então ele está sendo classificado, contado e, em seguida, classificado com base em sua contagem.

Eu estou no HP-UX, então algumas das minhas ferramentas são limitadas, mas eu sei que o awk pode fazer o que eu fiz com o sed. Alguém pode me ajudar a complicar meu comando?

    
por Malfist 30.06.2011 / 22:41

4 respostas

3
awk '/00[7-9]\.|0[1-9][0-9]\./ { # for lines matching the regex
       split($6, c, /:/)         # take the part of field 6 before the colon
       cs[ c[1] ]++              # and increment the counter for that string
     }
     END {                       # after all lines have been read
       for (c in cs) {           # step through the counters
         print cs[c], c          # and output the count followed by the string
                                 #   ("," adds a space automatically)
       }
     }' filename.log | sort -rn  # standard awk doesn't support sorting, sadly

Continuo espantado com o número de pessoas que aparentemente acreditam que nem awk nem sed podem fazer correspondência de padrões, por isso precisam adicionar uma invocação grep .

    
por 01.07.2011 / 00:11
1

Eu sou então será downvoted para isso ...

#!/usr/bin/env perl
use strict;

my %counts;
while (my $line = <>) {
    my @line = split(/\s+/, $line);
    if ($line[6] >= 7) {
        $line[5] =~ /(.+):/ and $counts{$1}++;
    }
}

my @sorted = sort {$counts{$b} <=> $counts{$a}} keys %counts;

printf("%7d\t%s\n", $counts{$_}, $_) for @sorted;
    
por 30.06.2011 / 23:06
1

Seu comando é um pouco frágil, pois falhará se o nome do arquivo tiver espaço nele. Caso contrário, seu comando não é muito ruim. É um pouco uma questão de gosto, mas acho que uma cadeia de comandos simples é muito mais fácil do que um comando complexo, como o grande awk que alguém postou. É quase provável que a programação seja em um estilo funcional.

Você pode, no entanto, alterar o grep para eliminar o awk e o sed, mas agora o regex é muito mais difícil de entender:


grep -P -o '(?<=\): ).+?(?=: 00[7-9]|0[1-9]|1)' | sort | uniq -c | sort -nr

Para explicar o regex, usamos o estilo perl re (-P param) e usamos o look behind (? < =) e look-ahead (? =) para isolar a correspondência exatamente com o nome da função. Note que o look-behind e o look-ahead são de largura zero, o que significa que eles não são considerados parte da partida, mas controlam o que a partida realmente será. Como a correspondência agora é exatamente o nome da função, podemos usar -o para dizer ao grep para imprimir apenas a string correspondente em vez da linha inteira. Eu acho que você deveria deixar o que você tem, a menos que você pense que um nome de arquivo com espaços é uma possibilidade.

    
por 01.07.2011 / 04:11
0

Enquanto estou nisso:

#!/bin/sh
grep -E '00[7-9]\.|0[1-9][0-9]\.' "$@" | awk '{print $6}' |
    sed 's/:$//' | sort | uniq -c | sort -rn

O comando original não é tão complicado, é a repetição para cada log que faz com que pareça. Cole-o em um arquivo de script (ou uma função), chame-o de sortbytime e aí - você tem um comando simples de uma palavra.

    
por 01.07.2011 / 00:17