Como você preserva um espaço em uma expressão literal em um valor de matriz no bash e como reduzir os resultados de um comando sort?

4

No contexto da busca por expressões em arquivos de log, eu estava me perguntando se havia uma maneira de quantificar e qualificar o conteúdo dos logs em /var/log de alguma forma. Em particular, o léxico usado para descrever condições de erro corresponde às expectativas gerais e como a terminologia varia entre os logs. Para investigar isso e validar essas expectativas, eu fiz este pequeno script que verifica, conta e classifica algumas coisas:

#!/bin/bash
# Meh - In /var/log, (1) for each file count lines which
# contain matches for expressions in the set; then, 
# (2), iterate files again and sort (all) words in a file  
# by descending frequency.

FILES="/var/log/* /var/log/**/* /var/log/**/**/*"
WORDS=(error fail.* wrong bad break panic abort.* disaster problem issue 'couldn'\''t' 'didn'\''t' 'wasn'\''t' 'shouldn'\''t' 'isn'\''t' 'don'\''t' "is'\ 'not" 'did\ not' die.* crash.* dump.* seg.* bug.* report.* status)

# Header section 1
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
printf 'Matches for expressions in set\n' 
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -

# (1)
for f in $FILES
do
    for w in ${WORDS[@]} 
    do 
        echo -n "$f [$w]:"
        grep -aci $w $f 2>/dev/null
    done
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
done

# Header section 2
printf 'Sorted occurences for all words in a file\n' 
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -

# (2)
for f in $FILES
do
    echo "[total number of lines: $(wc -l $f 2>/dev/null)]"
    cat $f 2>/dev/null | tr -c '[:alnum:]' '[\n*]' | tr -d '[:digit:]' | sort -f | uniq -ci | sort -fnr
    printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
done

exit

Por mais áspero e defeituoso, ele consegue o propósito de me dar um vislumbre da frequência e validar o conjunto até certo ponto. Por exemplo, aqui está um exemplo de saída para dmesg no meu sistema:

/var/log/dmesg [error]:27
/var/log/dmesg [fail.*]:7
/var/log/dmesg [wrong]:0
/var/log/dmesg [bad]:0
/var/log/dmesg [break]:0
/var/log/dmesg [panic]:1
/var/log/dmesg [abort.*]:0
/var/log/dmesg [disaster]:0
/var/log/dmesg [problem]:0
/var/log/dmesg [issue]:0
/var/log/dmesg [couldn't]:2
/var/log/dmesg [didn't]:0
/var/log/dmesg [wasn't]:0
/var/log/dmesg [shouldn't]:0
/var/log/dmesg [isn't]:0
/var/log/dmesg [don't]:0
/var/log/dmesg [is'\]:/var/log/dmesg ['not]:0     <------
/var/log/dmesg [did\]:/var/log/dmesg [not]:14     <------
/var/log/dmesg [die.*]:0
/var/log/dmesg [crash.*]:0
/var/log/dmesg [dump.*]:0
/var/log/dmesg [seg.*]:0
/var/log/dmesg [bug.*]:3
/var/log/dmesg [report.*]:1
/var/log/dmesg [status]:28

    [total number of lines: 1059 /var/log/dmesg]
      14784 
        306 usb
        220 pci
        133 x
        128 hub
        116 acpi
        113 d
        109 mem
         95 a
         94 uhci
         76 hcd
         76 device
         73 bus
         56 io
         55 to
         54 port
         54 e
         54 ata
         53 c
         51 power
         48 interface
         47 registered
         46 ehci
         40 system
         40 for
         38 new
         37 sda
         37 bridge
         36 on
         36 irq
         34 type
         34 reset
         34 probe
         34 nouveau
         33 v
         33 sd
         31 reserved
         30 memory
         29 f
         27 ports
         27 found
         27 error
         27 and
         26 resource
         26 reg
         26 input
         26 driver
         25 id
         25 i
         23 window
         23 disabled
         22 xc
         22 status
         22 from
         22 drm
         22 bit
         ...

Não é uma grande surpresa que "error" seja a "palavra problemática" mais importante na minha configuração ou que muitas entradas relacionadas a hardware apareçam em dmesg .

Perguntas

  • Como você preserva o espaço no valor das expressões literais feitas de duas palavras ("não é", "não fez") na lista de valores da matriz? Tentei "não" ou "não" e "muitas variações". Incerta sobre como aplicar as informações de this Q & A .
  • Como o segundo loop (2) acaba exibindo todas as ocorrências únicas de uma palavra em um log, como seu escopo pode ser restrito ou como devo descartar:
    • a saída consiste em caracteres únicos?
    • a saída consiste em ocorrências únicas?
  • Há alguma recomendação para apresentar visualmente a saída ou existe uma ferramenta que forneça mais funcionalidades para pesquisa ou formatação de dados de contagem e palavra?
por jus cogens prime 20.01.2014 / 09:39

2 respostas

4

Ao usar variáveis shell, você pode preservar caracteres de espaço (mais precisamente, impedir que os valores sejam divididos em palavras baseadas nos caracteres separadores de campo, que são enumerados na variável shell $ IFS) cercando as variáveis shell com aspas duplas.

for w in "${WORDS[@]}" 
do 
  echo -n "$f [$w]:"
  grep -aci "$w" $f 2>/dev/null
done

(Também não faria mal rodear $f com aspas, caso você encontre nomes de arquivos com espaços.)

As the second loop(2) ends up outputting every unique occurrences of a word in a log, how can its scope be restricted or how should I discard:

the output consisting of single chars?

Adicione grep .. no pipeline para incluir apenas linhas com 2 ou mais caracteres.

the output consisting of single occurrences?

Adicione -d ao uniq no pipeline, para que ele mostre apenas linhas duplicadas.

cat $f 2>/dev/null | tr -c '[:alnum:]' '[\n*]' | tr -d '[:digit:]' | sort -f | grep .. | uniq -dci | sort -fnr

Are there any recommendations for visually presenting the output, or is there a tool which provides further functionality for either searching or formatting count and word data?

Há um monte de aplicativos por aí que varrem e resumem ocorrências interessantes em arquivos de log, alguns gratuitos, outros comerciais. Não tenho certeza se podemos dar recomendações gerais, mas se você puder dar exemplos de consultas que gostaria de criar ou exibir os formatos que gostaria de ver, talvez possamos responder a esses tipos de perguntas.

    
por 20.01.2014 / 13:32
2

Preserve o caractere de espaço alterando o Inter Field Seperator do IFS (consulte o bash)

Aqui eu crio WORDS2 depois de configurar o IFS para sublinhar, então a matriz é estabelecida usando o sublinhado entre os campos e o espaço em did not e should not não é de escape.

SAVE_IFS="$IFS"
IFS=_
WORDS2=(error_fail.*_wrong_bad_break_panic_abort.*_disaster_problem_issue_'couldn'\''t'_'didn'\''t'_'wasn'\''t'_'shouldn'\''t'_'isn'\''t'_'don'\''t'_"is not"_'did not'_die.*_crash.*_dump.*_seg.*_bug.*_report.*_status)
IFS=$SAVE_IFS

Então, no loop novamente, o IFS é trocado

IFS=_
for w in ${WORDS2[@]}
do
    echo -n "$f [$w]:"
    grep -aci $w $f 2>/dev/null
done
IFS=$SAVE_IFS

Eu obtive esta saída de / var / log / messages

/var/log/messages [error]:0
/var/log/messages [fail.*]:0
/var/log/messages [wrong]:0
/var/log/messages [bad]:0
/var/log/messages [break]:4
/var/log/messages [panic]:0
/var/log/messages [abort.*]:0
/var/log/messages [disaster]:0
/var/log/messages [problem]:0
/var/log/messages [issue]:0
/var/log/messages [couldn't]:0
/var/log/messages [didn't]:0
/var/log/messages [wasn't]:0
/var/log/messages [shouldn't]:0
/var/log/messages [isn't]:0
/var/log/messages [don't]:0
/var/log/messages [is not]:2
/var/log/messages [did not]:0
/var/log/messages [die.*]:0
/var/log/messages [crash.*]:0
/var/log/messages [dump.*]:0
/var/log/messages [seg.*]:0
/var/log/messages [bug.*]:1
/var/log/messages [report.*]:0
/var/log/messages [status]:4

Restringindo palavras pequenas

Eu adicionei este filtro sed para excluir linhas vazias e palavras de 1 a 2 caracteres antes de começar a classificar e contar

... | sed -e '/^$/d' -e '/^[a-zA-Z]\{1,2\}$/d' | sort -f ...
    
por 20.01.2014 / 13:37