Extrair valores do texto formatado

0

Existe uma maneira fácil de extrair variáveis de um arquivo de texto?

Por exemplo dada a saída de ab :

This is ApacheBench, Version 2.3 <$Revision: 1638069 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking bar (be patient)
Finished 1206 requests


Server Software:        Jetty(9.0.z-SNAPSHOT)
Server Hostname:        bar
Server Port:            5500

Document Path:          /foo/1
Document Length:        148 bytes

Concurrency Level:      15
Time taken for tests:   30.041 seconds
Complete requests:      1206
Failed requests:        0
Total transferred:      359686 bytes
HTML transferred:       178636 bytes
Requests per second:    40.15 [#/sec] (mean)
Time per request:       373.643 [ms] (mean)
Time per request:       24.910 [ms] (mean, across all concurrent requests)
Transfer rate:          11.69 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       47  108  36.0     98     328
Processing:    73  264 782.5    150    7951
Waiting:       73  255 721.5    148    7886
Total:        129  371 783.5    259    8039

Percentage of the requests served within a certain time (ms)
  50%    259
  66%    293
  75%    324
  80%    340
  90%    413
  95%    525
  98%    683
  99%   6421
 100%   8039 (longest request)

Gostaria de extrair valores (correspondendo name: value , veja o exemplo abaixo) e atribuí-los às variáveis em uma etapa. (Eu sei que ab pode exportar alguns dados para o csv, mas o resto só está disponível como texto formatado.)

O melhor que encontrei até agora é:

path=$(cat text|grep 'Document Path:'|awk -F: '{ split($2, z, " "); print z[1]}')
total=$(cat text|grep 'Total transferred:'|awk -F: '{ split($2, z, " "); print z[1]}')
#[...]

Mas isso parece ser um pouco repetitivo e a ala awk - existe uma maneira mais fácil ou uma ferramenta mais adequada para esse trabalho?

    
por laktak 12.01.2016 / 16:02

2 respostas

2

Eu procuraria todas as linhas com 1-4 palavras e, em seguida, : , substitua os espaços entre as palavras por sublinhados e os imprima como variable=value pairs. Você pode então passar a coisa toda através de eval para defini-los. Por exemplo:

$ awk -F': *' '/^(\S+\s*){1,4}:/{gsub(/ /,"_",$1);print $1"=\""$2"\""}' file
Server_Software="Jetty(9.0.z-SNAPSHOT)"
Server_Hostname="bar"
Server_Port="5500"
Document_Path="/foo/1"
Document_Length="148 bytes"
Concurrency_Level="15"
Time_taken_for_tests="30.041 seconds"
Complete_requests="1206"
Failed_requests="0"
Total_transferred="359686 bytes"
HTML_transferred="178636 bytes"
Requests_per_second="40.15 [#/sec] (mean)"
Time_per_request="373.643 [ms] (mean)"
Time_per_request="24.910 [ms] (mean, across all concurrent requests)"
Transfer_rate="11.69 [Kbytes/sec] received"
Connect="47  108  36.0     98     328"
Processing="73  264 782.5    150    7951"
Waiting="73  255 721.5    148    7886"
Total="129  371 783.5    259    8039"

O -F': * define o separador de campo como : seguido por 0 ou mais espaços. Em seguida, o script verifica se essa linha corresponde de 1 a 4 ocorrências de uma sequência de caracteres não espaciais ("palavras") seguidas por 0 ou mais espaços e, em seguida, : . Eu uso 4 por causa dessa linha:

Time taken for tests:   30.041 seconds

Em seguida, para as linhas correspondentes, substitua todos os espaços no primeiro campo por sublinhados ( gsub(/ /,"_",$1) ), imprima o primeiro campo, um = e o segundo campo cotado. Como as sequências nuas precisam ser citadas para awk para imprimi-las, para imprimir uma cotação $2 , as cotações precisam ser escapadas: " \"" .

Se isso produzir a saída desejada, você poderá usar eval para ler as variáveis:

$ eval $(awk -F': *' '/^(\S+\s*){1,4}:/{gsub(/ /,"_",$1);print $1"=\""$2"\""}' file)
$ echo $Transfer_rate 
11.69 [Kbytes/sec] received

Ou apenas forneça:

. <(awk -F': *' '/^(\S+\s*){1,4}:/{gsub(/ /,"_",$1);print $1"=\""$2"\""}' file)

Importante : isso pode ser perigoso. eval ou fonte do arquivo simplesmente executará qualquer código que você fornecer. Não irá verificar se é perigoso. Se o script awk retornar algo perigoso como rm ~/* , por algum motivo, o eval ficará feliz em executá-lo. Portanto, sempre verifique qual saída você obtém antes de executar o acima. Esse também é o caso da resposta aceita. É sempre perigoso executar cegamente o código retornado por outro programa.

Os itens acima funcionarão para% GNUawk, mas não para implementações awk mais simples. Se não funcionar no seu sistema, tente isso:

. <(awk -F': *' '/.*\s*:   *:/{gsub(/ /,"_",$1);print $1"=\""$2"\""}' file)
    
por 14.01.2016 / 11:42
4

Eu geralmente uso o seguinte padrão:

. <(
    awk 'BEGIN{print "shellvarname=\"value\""}'
)

Isso usa awk para gerar algumas instruções que funcionariam na sintaxe de atribuição de variáveis shells. Este resultado é originado ( . ).

Em sua solicitação específica, isso seria uma opção:

. <(
    awk -F': *' '
      /Document Path/{printf "%s=\"%s\"\n", "path", $2}
      /Total transferred/{printf "%s=\"%s\"\n", "total", $2}
    ' file
)

ou um pouco mais curto

. <(
    awk '
      /Document Path/{printf "%s=\"%s\"\n", "path", $3}
      /Total transferred/{printf "%s=\"%s\"\n", "total", $3}
    ' file
)
    
por 12.01.2016 / 16:20