:::: MENU ::::

quinta-feira, 8 de março de 2012

O Case

Outro dia o amigo ElCheVive68 me pediu para fazer um script, que o auxiliaria em uma de suas tarefas, já que o equipamento comprado para tal demanda estava se confundindo nos cálculos :)

Nesse post, abordaremos apenas o tratamento do arquivo e como realizar a limpeza + cálculo dos valores.


Os arquivos

Estes continham em média 360 linhas, com valores em V/m (volts/minuto) e mV/m (microvolts/minuto). Era esperado que mostrasse:
  • A média do campo elétrico
  • O campo magnético
  • A potência

Eles são mais ou menos assim:
1sec  Logging at: Feb 04, 2012 Thu 10:32:36 Temp=+29.1 C
7.20V/m
7.23V/m
7.46V/m
[...]
7.46V/m
AUZ V/m
AUZ V/m
2.30V/m
2.18V/m
1.48V/m
 972mV/m
 972mV/m
 755mV/m
[...]
Recorded samples: 00366  Thu 10:38:44 Temp=+29.4 C
Max:14.6V/m   Avg:2.61V/m 

Segue arquivos de exemplo:

Baixe os arquivos e vamos começar a brincadeira :)
Lembrem de deixar esses arquivos no mesmo diretório do script. Eu sei que poderia ter melhorado isso, mas tô com preguiça ( mimimimimimimi )

HANDS ON!


Primeiro, vamos arrumar um lugar pra fazer nossa brincadeira:

[ -d output ] || mkdir output

Depois, vamos criar uma função, para deixar tudo bem organizado:
tratamento(){
LIST=output/$FILE
VERIFIED=output/${FILE}_verificado.txt
cp $FILE $LIST.out
dos2unix $LIST.out 


[ -d output ] || mkdir output irá criar um diretório chamado output, caso ele não exista;
OUTPUT="resultado.csv" é a variável que criará o arquivo com o resultado;
tratamento(){ é como iniciamos uma função em Shellscript;
LIST=output/$FILE é a variável que manterá o nome do arquivo que será tratado pelo script;
VERIFIED=output/${FILE}_verificado.txt gerará a saída dos arquivos já tratados.
 cp $FILE $LIST.out cria uma cópia do arquivo a ser tratado. Sempre trabalharemos com a cópia;
 dos2unix $LIST.out irá converter os arquivos de texto do padrão windows para o Unix - essa parte é MUITO IMPORTANTE.




Se o objetivo é realizar cálculos com os valores, então, temos de analisar o arquivo-fonte e deixar somente o que é necessário, no caso, os números. A melhor ferramenta para realizar essa limpeza é, na minha opinião, o sed:

 sed -i 's/ //g;/AUZ/d;/^$/d;/Recordedsamples/d;/Logging/d;/Max/d;' $LIST.out
Aqui, estamos:
sed -i - altera diretamente o arquivo de entrada;
's/ //g; - remove todos os espaços em branco;
/AUZ/d; - remove as linhas que contém o valor AUZ;
/^$/d; - Nas expressões regulares, '^' significa início de linha e '$' final de linha. Então, se juntarmos os dois, teremos uma linha vazia! Caso haja alguma em nosso arquivo, ela será removida.
/Recordedsamples/d; - Observem que no final do arquivo, temos esse trecho. Como o sed já removeu os espaços em branco, temos de ficar atentos às strings posteriores.
/Logging/d; - É o cabeçalho do arquivo. Inútil.
/Max/d;' - Última linha do arquivo. Inútil.
$LIST.out - O arquivo de entrada.
Com isso, teremos o arquivo apenas com os valores das cargas.

Mas, temos um pequeno problema: Temos valores em mV/m e em V/m!
Esse ponto é fácil de resolver, basta acharmos o valor total em mV/m e dividir por 1000. Com isso teremos o seu valor em V/m.
Apenas como forma de prevenção a outros problemas, devemos prevenir o caso de termos arquivos com apenas mV/m ou V/m. Faremos isso de maneira simples:
Usa-se o grep buscando, por exemplo, por mV/m. Se ele encontrar, o valor de retorno será 0, pois o comando foi executado com sucesso. Caso ele não encontre nenhum valor igual ao padrão especificado, ele retornará 1. Então, de posse dessas informações...


grep 'mV/m' $LIST.out > /dev/null ; [ $? = 0 ] && \
 MVM=$( bc <<< "scale = 3; $( grep 'mV/m' $LIST.out | tr -d 'mV/m' | paste -sd+ | bc ) / 1000 " ) \
 || MVM=0
 grep -E '*[0-9]V/m' $LIST.out > /dev/null ; [ $? = 0 ] && \
        VM=$( egrep '*[0-9]V/m' $LIST.out | tr -d 'V/m' | paste -sd+ | bc ) || VM=0


Então, aqui temos:
grep 'mV/m' $LIST.out > /dev/null ; [ $? = 0 ]: Um grep buscando por 'mV/m' no arquivo presente na variável com seu STDOUT direcionado para o /dev/null, uma maneira de ocultar a saída em tela. Caso o grep tenha sucesso em sua busca, ele retornará 0 na variável $?, e em seguida:
Fará uma nova busca pelo mesmo valor (mV/m), porém, redirecionará a saída para o tr;
O tr irá remover todos os padrões 'mV/m' encontrados no arquivo e jogará a saída para o paste;
O paste irá mudar a posição dos valores. Ao invés de um valor por linha, ele os colocará lado a lado e, utilizando como separador, um sinal de soma ( + ).
O bc, por sua vez, receberá o redirecionamento do paste, obtendo os valores, parecido com essa saída:
972+972+755+435+358+307+704+716+601+371+345+460+499+473+409+320+384+422+473+396
+396+371+371+371+371+358+512+499+486+332+358+371+371+358+371+371+396+384+345+33
2+345+358+384+358+332+345+358+358+332... 

Bom, aí o bc só soma e vai pra galera :)
Se ele não encontrar nenhum valor em mV/m, ele irá atribuir 0 a variável MVM, pois se esta estiver vazia, o bc não irá calcular e apresentará um erro.
Percebam também que existe um / 1000. Com isso, eu consigo igualar o valor dos mV/m aos V/m, facilitando o meu cálculo pela média do arquivo.

No trecho buscando V/m, eu faço algo parecido, exeto pelo fato que, deixo claro, utilizando expressões regulares, que quero apenas valores numéricos imediatamente seguidos por V/m ( *[0-9]V/m ). Do mesmo modo, caso não encontre nada em V/m, atribua 0 a variável VM.

De posse das variáveis MVM e VM com seus devidos valores, vamos ao final:

SUM=$( bc <<< "$MVM + $VM " )
LINHAS=$( wc -l $LIST.out | cut -f1 -d' ' )
MED=$( bc <<< "scale = 3; $SUM / $LINHAS" )
HAV=$( bc <<< "scale = 6; $MED / 377" )
SEQ=$( bc <<< "scale = 6; $MED ^ 2 /377" )

SUM é a soma dos valores de MVM (já convertidos em V/m) e VM;
LINHAS é a quantidade de valores válidos no arquivo;
MED é a média do campo elétrico;
HAV é o campo magnético e;
SEQ é a potência.

Depois disso, é só printar na tela, fechar a função e adicionar um laço para ler os arquivos que serão usados como STDIN :D

echo -e "Total = $SUM
Amostras = $LINHAS
Media = $MED
H = $HAV
Seq = $SEQ" > $VERIFIED

}

for FILE in $( ls *.TXT ); do
   tratamento
done

Prontinho! Seu script para fazer cálculos estranhos (hauhauhauhaa) está pronto! Todos os resultados estarão no diretório output (criado no diretório corrente do script).
Agora é só juntar tudo e... FINISH!


#!/bin/bash
# Rauhmaru, rauhmaru@opensuse.org
# ElCheVive68, elchevive68@opensuse.org
# Calcula valores
#
[ -d output ] || mkdir output
OUTPUT="resultado.csv"
tratamento(){
LIST=output/$FILE
VERIFIED=output/${FILE}_verificado.txt
 cp $FILE $LIST.out
 dos2unix $LIST.out
## LIMPEZA
 sed -i 's/ //g;/AUZ/d;/^$/d;/Recordedsamples/d;/Logging/d;/Max/d;' $LIST.out
## CALCULOS
 grep 'mV/m' $LIST.out > /dev/null ; [ $? = 0 ] && \
 MVM=$( bc <<< "scale = 3; $( grep 'mV/m' $LIST.out | tr -d 'mV/m' | paste -sd+ | bc ) / 1000 " ) \
 || MVM=0
 grep -E '*[0-9]V/m' $LIST.out > /dev/null ; [ $? = 0 ] && \
        VM=$( egrep '*[0-9]V/m' $LIST.out | tr -d 'V/m' | paste -sd+ | bc ) || VM=0
 SUM=$( bc <<< "$MVM + $VM " )
 LINHAS=$( wc -l $LIST.out | cut -f1 -d' ' )
 MED=$( bc <<< "scale = 3; $SUM / $LINHAS" )
 HAV=$( bc <<< "scale = 6; $MED / 377" )
 SEQ=$( bc <<< "scale = 6; $MED ^ 2 /377" )
## PRINTS
 echo -e "Total = $SUM
Amostras = $LINHAS
Media = $MED
H = $HAV
Seq = $SEQ" > $VERIFIED
 rm $LIST.out
}
for FILE in $( ls *.TXT ); do
tratamento
done
echo -e "Arquivo\tTotal\tAmostras\tMedia\tH\tSeq" > $OUTPUT
for i in $( ls output/*_verificado.txt ); do
LINHA=$( awk '{ print $NF}' $i | paste -sd'\t')
echo -e "$(basename $i | awk -F'.' '{ print $1}' )\t$LINHA" >> $OUTPUT
done

Pra dar mais uma incrementada, no final eu coloco os arquivos em um .csv, para que sejam visualizados no Calc ou Excel. Cada arquivo estará em uma linha, e os seus valores agrupados em coluna. :)

Um abraço!





0 comentários:

Postar um comentário

Só não vale xingar a mãe ou puxar cabelo nos comentários =)

Posts populares