Analisa um arquivo txt baseado em um delimitador específico e transforme-o em um arquivo CSV

4

Eu tenho o seguinte arquivo chamado OpenSimStats.txt:

TestreportsRootAgentCount=0agent(s)
TestreportsChildAgentCount=0childagent(s)
TestreportsGCReportedMemory=10MB(Global)
TestreportsTotalObjectsCount=0Object(s)
TestreportsTotalPhysicsFrameTime=0ms
TestreportsPhysicsUpdateFrameTime=0ms
TestreportsPrivateWorkingSetMemory=2144MB(Global)
TestreportsTotalThreads=0Thread(s)(Global)
TestreportsTotalFrameTime=89ms
TestreportsTotalEventFrameTime=0ms
TestreportsLandFrameTime=0ms
TestreportsLastCompletedFrameAt=25msago
TestreportsTimeDilationMonitor=1
TestreportsSimFPSMonitor=55.3333320617676
TestreportsPhysicsFPSMonitor=55.4766654968262
TestreportsAgentUpdatesPerSecondMonitor=0persecond
TestreportsActiveObjectCountMonitor=0
TestreportsActiveScriptsMonitor=0
TestreportsScriptEventsPerSecondMonitor=0persecond
TestreportsInPacketsPerSecondMonitor=0persecond
TestreportsOutPacketsPerSecondMonitor=0persecond
TestreportsUnackedBytesMonitor=0
TestreportsPendingDownloadsMonitor=0
TestreportsPendingUploadsMonitor=0
TestreportsTotalFrameTimeMonitor=18.18239402771ms
TestreportsNetFrameTimeMonitor=0ms
TestreportsPhysicsFrameTimeMonitor=0.0106373848393559ms
TestreportsSimulationFrameTimeMonitor=0.17440040409565ms
TestreportsAgentFrameTimeMonitor=0ms
TestreportsImagesFrameTimeMonitor=0ms
TestreportsSpareFrameTimeMonitor=18.1818199157715ms
TestreportsLastReportedObjectUpdates=0
TestreportsSlowFrames=1

Eu quero transformar esse arquivo em um arquivo CSV como o seguinte:

TestreportsRootAgentCount,TestreportsChildAgentCount,...,TestreportsSlowFrames
0,0,10,0,0...,1

Por que eu quero dizer:

  1. retire todas as palavras antes e depois de um delimitador, neste caso, o delimitador é "="
  2. Coloque todas as palavras à esquerda do delimitador em uma linha separadas por vírgulas
  3. Inserir uma nova linha no final
  4. Em seguida, coloque o depois do delimitador ( = ) - os números apenas (sem as unidades ou caracteres após os números) em outra linha em que esses números são separados por vírgulas.
  5. Em seguida, insira uma nova linha

Alguma idéia / sugestão de como isso pode ser feito no shell script do Linux? Usando sed ou gawk?

    
por HB87 18.07.2016 / 15:28

4 respostas

9

Os 9 caminhos para o esclarecimento do OpenSim:

Com sed e alguma mágica de concha:

sed 's/=.*//' OpenSimStats.txt | paste -sd, >out.csv
sed 's/.*=//; s/[^0-9]*$//' OpenSimStats.txt | paste -sd, >>out.csv

Com sed , sem mágica de concha:

sed -n 's/=.*//; 1{ h; b; }; $! H; $ { x; s/\n/,/g; p; }' OpenSimStats.txt >out.csv
sed -n 's/.*=//; 1{ s/[0-9]*$//; h; b; }; s/[^0-9]*$//; $! H; $ { x; s/\n/,/g; p; }' OpenSimStats.txt >>out.csv

Com mágica de shell e um pouquinho de sed :

paste -sd, <(cut -d= -f1 OpenSimStats.txt) <(cut -d= -f2 OpenSimStats.txt | sed 's/[^0-9]*$//')

Com cut e alguma mágica de concha:

cut -d= -f1 OpenSimStats.txt | paste -sd, >out.csv
cut -d= -f2 OpenSimStats.txt | sed 's/[^0-9]*$//' | paste -sd, >>out.csv

Com o GNU datamash :

sed 's/=/,/; s/[^0-9]*$//' OpenSimStats.txt | datamash -t, transpose

com perl :

perl -lnE 's/\D+$//o;
    ($a, $b) = split /=/;
    push @a, $a; push @b, $b;
    END { $, = ","; say @a; say @b }' OpenSimStats.txt

com grep :

grep -o '^[^=]*' OpenSimStats.txt | paste -sd, >out.csv
egrep -o '[0-9.]+' OpenSimStats.txt | paste -sd, >>out.csv

com bash :

#! /usr/bin/env bash
line1=()
line2=()
while IFS='=' read -r a b; do
    line1+=("$a")
    [[ $b =~ ^[0-9.]+ ]]
    line2+=("$BASH_REMATCH")
done <OpenSimStats.txt
( set "${line1[@]}"; IFS=,; echo "$*" ) >out.csv
( set "${line2[@]}"; IFS=,; echo "$*" ) >>out.csv

com awk :

awk -F= '
    NR==1 { a = $1; sub(/[^0-9]+$/, "", $2); b = $2; next }
    { a = a "," $1; sub(/[^0-9]+$/, "", $2); b = b "," $2 }
    END { print a; print b }' OpenSimStats.txt

10º caminho do bônus para nerds de dados, com csvtk :

csvtk replace -d= -f 2 -p '\D+$' -r '' <OpenSimStats.txt | csvtk transpose

Bônus no 11º caminho com vim :

:%s/\D*$//
:%s/=/\r/
qaq
:g/^\D/y A | normal dd
:1,$-1 s/\n/,/
"aP
:2,$-2 s/\n/,/
:d 1
:w out.csv
    
por 18.07.2016 / 16:01
8

awk para ajudar você:

awk -F= '{a[NR,1]=$1;a[NR,2]=$2}
         END{
            for(i=1; i<NR; i++){
                printf a[i,1] ","
            }
            print a[i,1]; 
            for(i=1; i<NR; i++){
                printf "%s", a[i,2]+0
            } 
            print a[i,2];
        }' file

A matriz a é preenchida com a chave $1 na primeira coluna e com o valor $2 na segunda coluna.

Quando todas as linhas forem lidas, percorra duas vezes todos os elementos das matrizes para mostrar a chave e os valores.

    
por 18.07.2016 / 15:43
3

Aqui está uma solução Perl:

$ perl -F= -lae '$F[1]=~s/[^0-9]//g; push @h,$F[0]; push @l,$F[1]; 
                  END{print join ",",@h; print join ",",@l}' OpenSimStats.txt 
TestreportsRootAgentCount,TestreportsChildAgentCount,TestreportsGCReportedMemory,TestreportsTotalObjectsCount,TestreportsTotalPhysicsFrameTime,TestreportsPhysicsUpdateFrameTime,TestreportsPrivateWorkingSetMemory,TestreportsTotalThreads,TestreportsTotalFrameTime,TestreportsTotalEventFrameTime,TestreportsLandFrameTime,TestreportsLastCompletedFrameAt,TestreportsTimeDilationMonitor,TestreportsSimFPSMonitor,TestreportsPhysicsFPSMonitor,TestreportsAgentUpdatesPerSecondMonitor,TestreportsActiveObjectCountMonitor,TestreportsActiveScriptsMonitor,TestreportsScriptEventsPerSecondMonitor,TestreportsInPacketsPerSecondMonitor,TestreportsOutPacketsPerSecondMonitor,TestreportsUnackedBytesMonitor,TestreportsPendingDownloadsMonitor,TestreportsPendingUploadsMonitor,TestreportsTotalFrameTimeMonitor,TestreportsNetFrameTimeMonitor,TestreportsPhysicsFrameTimeMonitor,TestreportsSimulationFrameTimeMonitor,TestreportsAgentFrameTimeMonitor,TestreportsImagesFrameTimeMonitor,TestreportsSpareFrameTimeMonitor,TestreportsLastReportedObjectUpdates,TestreportsSlowFrames
0,0,10,0,0,0,2144,0,89,0,0,25,1,553333320617676,554766654968262,0,0,0,0,0,0,0,0,0,1818239402771,0,00106373848393559,017440040409565,0,0,181818199157715,0,1

O -a sinalizador faz com que perl aja como awk e divida cada linha de entrada no delimitador de campo fornecido por -F (um = aqui) na matriz @F . O -l adiciona \n a cada print e o -e é o script que será executado em cada linha.

  • $F[1]=~s/[^0-9]//g; : remove todos os caracteres não numéricos do 2º campo (os arrays começam a contar a 0, então $F[1] é o segundo campo).
  • push @h,$F[0]; push @l,$F[1]; : envia o primeiro campo para o array @h e o segundo (agora que os não-dígitos foram removidos) para o array @l .
  • END{} : isso é executado uma vez, após todo o arquivo de entrada ter sido processado.
  • print join ",",@h; : junte a matriz @h com , e imprima-a.
  • print join ",",@l : como acima, mas para @l .
por 18.07.2016 / 16:13
1

Eu sei que esta pergunta foi respondida, eu imaginei apenas adicionar meus dois centavos de como fazer isso em um (feio) bash one-liner

Resultado final

echo -e $(cut -d"=" -f1 OpenSimStats.txt | tr '\n' ',' | sed 's/,$/\n/')$(sed -r 's/.*=([0-9]*).*/,/g' OpenSimStats.txt | tr -d '\n' | sed 's/,$//')

Isso produzirá o seguinte resultado que você pediu em uma linha:

TestreportsRootAgentCount,TestreportsChildAgentCount,TestreportsGCReportedMemory,TestreportsTotalObjectsCount,TestreportsTotalPhysicsFrameTime,TestreportsPhysicsUpdateFrameTime,TestreportsPrivateWorkingSetMemory,TestreportsTotalThreads,TestreportsTotalFrameTime,TestreportsTotalEventFrameTime,TestreportsLandFrameTime,TestreportsLastCompletedFrameAt,TestreportsTimeDilationMonitor,TestreportsSimFPSMonitor,TestreportsPhysicsFPSMonitor,TestreportsAgentUpdatesPerSecondMonitor,TestreportsActiveObjectCountMonitor,TestreportsActiveScriptsMonitor,TestreportsScriptEventsPerSecondMonitor,TestreportsInPacketsPerSecondMonitor,TestreportsOutPacketsPerSecondMonitor,TestreportsUnackedBytesMonitor,TestreportsPendingDownloadsMonitor,TestreportsPendingUploadsMonitor,TestreportsTotalFrameTimeMonitor,TestreportsNetFrameTimeMonitor,TestreportsPhysicsFrameTimeMonitor,TestreportsSimulationFrameTimeMonitor,TestreportsAgentFrameTimeMonitor,TestreportsImagesFrameTimeMonitor,TestreportsSpareFrameTimeMonitor,TestreportsLastReportedObjectUpdates,TestreportsSlowFrames
0,0,10,0,0,0,2144,0,89,0,0,25,1,55,55,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,18,0,1

Divisão do código

Aqui está o resumo do que está acontecendo.

Eco exterior

echo -e $()$()

Eco os resultados de dois comandos aninhados que são executados primeiro, o -e é para que o \n impresso seja convertido em novas linhas no resultado final.

Primeiro comando

cut -d"=" -f1 OpenSimStats.txt | tr '\n' ',' | sed 's/,$/\n/'

O primeiro comando aninhado. Use = como um delimitador para extrair todo o texto como uma coluna (série de valores \n -separados). Substitua todos os \n por , , em seguida, substitua a vírgula final por um \n novamente (caso contrário, o último valor será seguido por uma vírgula).

Este comando por si só produz a seguinte saída:

TestreportsRootAgentCount,TestreportsChildAgentCount,TestreportsGCReportedMemory,TestreportsTotalObjectsCount,TestreportsTotalPhysicsFrameTime,TestreportsPhysicsUpdateFrameTime,TestreportsPrivateWorkingSetMemory,TestreportsTotalThreads,TestreportsTotalFrameTime,TestreportsTotalEventFrameTime,TestreportsLandFrameTime,TestreportsLastCompletedFrameAt,TestreportsTimeDilationMonitor,TestreportsSimFPSMonitor,TestreportsPhysicsFPSMonitor,TestreportsAgentUpdatesPerSecondMonitor,TestreportsActiveObjectCountMonitor,TestreportsActiveScriptsMonitor,TestreportsScriptEventsPerSecondMonitor,TestreportsInPacketsPerSecondMonitor,TestreportsOutPacketsPerSecondMonitor,TestreportsUnackedBytesMonitor,TestreportsPendingDownloadsMonitor,TestreportsPendingUploadsMonitor,TestreportsTotalFrameTimeMonitor,TestreportsNetFrameTimeMonitor,TestreportsPhysicsFrameTimeMonitor,TestreportsSimulationFrameTimeMonitor,TestreportsAgentFrameTimeMonitor,TestreportsImagesFrameTimeMonitor,TestreportsSpareFrameTimeMonitor,TestreportsLastReportedObjectUpdates,TestreportsSlowFrames

Segundo comando

sed -r 's/.*=([0-9]*).*/,/g' OpenSimStats.txt | tr -d '\n' | sed 's/,$//'

O segundo comando aninhado. Remova todo o texto em torno do número desejado em cada linha, resultando em uma coluna de números (valores \n separados). Substitua todos os \n por , e, em seguida, remova a vírgula final.

Isso produz o seguinte resultado:

0,0,10,0,0,0,2144,0,89,0,0,25,1,55,55,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,18,0,1

O topo de uma linha só combina essas três peças em uma única linha para produzir o resultado final.

    
por 18.07.2016 / 21:52