从格式化文本中提取值

从格式化文本中提取值

有没有一种简单的方法从文本文件中提取变量?

例如,给出以下输出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)

我想提取值(匹配name: value,请参见下面的示例)并一步将它们分配给变量。 (我知道ab可以将一些数据导出到 csv,但其余数据只能作为格式化文本使用。)

到目前为止我发现的最好的是:

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]}')
#[...]

但这似乎有点重复awk病房 - 有没有更简单的方法或更适合这项工作的工具?

答案1

我一般使用以下模式:

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

这用于awk生成一些可在 shell 变量赋值语法中使用的语句。该结果来源于( .)。

根据您的具体要求,这将是一个选项:

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

或者短一点

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

答案2

我会查找所有包含 1-4 个单词的行,然后使用:,用下划线替换单词之间的空格并将它们作为variable=value对打印。然后您可以传递整个内容来eval设置它们。例如:

$ 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"

-F': *字段分隔符设置为:后跟 0 个或多个空格。然后,该脚本检查该行是否匹配出现 1 到 4 次的非空格字符(“单词”)字符串,后跟 0 个或多个空格,然后是:。我使用 4 因为这一行:

Time taken for tests:   30.041 seconds

然后,对于匹配的行,将第一个字段中的所有空格替换为下划线 ( gsub(/ /,"_",$1)),然后打印第一个字段、an=和引用的第二个字段。因为裸字符串需要加引号才能awk打印它们,所以为了打印带引号的$2,需要对引号进行转义:" \""

如果这产生了您想要的输出,您现在可以用来eval读取变量:

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

或者,直接获取它:

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

重要的: 这可能很危险。eval或者获取该文件将简单地执行您提供的任何代码。它不会检查它是否危险。如果脚本由于某种原因awk返回一些危险的东西,那么将愉快地运行它。因此,在运行上述命令之前,请务必检查得到的输出。已接受的答案也是如此。盲目执行另一个程序返回的代码总是很危险的。rm ~/*eval


上面的内容适用于 GNU,awk但不适用于更简单的awk实现。如果它在您的系统上不起作用,请尝试以下操作:

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

相关内容