类似字符串,结果不同

时间:2016-02-22 00:21:53

标签: json bash awk

我正在创建一个Bash脚本来解析网页上的空气污染水平: http://aqicn.org/city/beijing/m/

文件中有很多东西,但这是相关的一点:

  

" iaqi":[{" P":" PM25"" V":[ 59 ,21,112],"我":"北京时间pm25(罚款   颗粒物质)由美国大使馆北京空气质量公司测量   监控   (\ u7f8e \ u56fd \ u9a7b \ u5317 \ u4eac \ u5927 \ u4f7f \ u9986 \ u7a7a \ u6c14 \ u8d28 \ u91cf \ u76d1 \ u6d4b)。   使用EPA将值从\ u00b5g / m3转换为AQI级别   标准"},{" P":" PM10"" V":[ 15 下,5,69] ,"我":"北京时间10点   (可吸入颗粒物)由北京环境测量   保护监测中心

我希望脚本解析并显示2个数字:当前PM2.5和PM10级别(上段中粗体中的数字)。

CITY="beijing"
AQIDATA=$(wget -q 0 http://aqicn.org/city/$CITY/m/ -O -)

PM25=$(awk -v FS="(\"p\":\"pm25\",\"v\":\\\[|,[0-9]+)" '{print $2}' <<< $AQIDATA)
PM100=$(awk -v FS="(\"p\":\"pm10\",\"v\":\\\[|,[0-9]+)" '{print $2}' <<< $AQIDATA)

echo $PM25 $PM100

即使我可以正确显示PM2.5级别,我也无法显示PM10级别。我无法理解为什么,因为字符串是相似的。

任何人都可以解释一下吗?

4 个答案:

答案 0 :(得分:3)

以下方法基于两个步骤:

(1)提取相关的JSON;

(2)使用JSON感知工具从JSON中提取相关信息 - jq

(1)理想情况下,Web服务将提供允许直接获取JSON的JSON API,但由于您具有用于浏览器查看的URL,因此需要某种形式的屏幕抓取。这种方法有一定的脆弱性,所以我在这里只提供目前有用的东西:

wget -O - http://aqicn.org/city/beijing/m |
  gawk 'BEGIN{RS="function"}
         $1 ~/getAqiModel/ {
         sub(/.*var model=/,"");
         sub(/;return model;}/,"");
         print}'

(可以使用支持多字符RS的gawk或awk;如果你有另一个awk,那么首先拆分&#34; function&#34;,使用例如:

sed $&#39; s / function / \\\ n / g&#39; #3反斜杠)

上述输出可以通过管道输出到以下jq命令,该命令执行上面(2)中设想的过滤。

(2)

jq -c '.iaqi | .[]
| select(.p? =="pm25" or .p? =="pm10") | [.p, .v[0]]'

结果:

["pm25",59]
["pm10",15]

答案 1 :(得分:2)

我认为您的问题是您有一行HTML文件,其中包含一个脚本,其中包含一个包含您要查找的数据的变量。

您的字段分隔符 "p":"pm100", "v":[ 逗号和一些数字。

对于pm25,这是有效的,因为它是第一个,并且在它之前没有出现,21或类似的内容。

但是,对于pm10,有一些与pm25相关联。因此 second 字段包含,21,112之间的空字符串

@karakfa有一个似乎有效的黑客攻击 - 但他并没有很好地解释为什么它会起作用。

他所做的是使用awk的记录分隔符(通常是换行符)并将其设置为:,[。所以在你的情况下,其中一个记录是"pm25",因为它前面有一个冒号,它是一个分隔符,后面跟一个逗号,也是一个分隔符。

一旦它到达匹配的内容("pm25"),它就会将计数器设置为4.然后,对于这个和下一个记录,它会计算此计数器。 "pm25"本身"v":[之间的空字符串,最后在使用您要输出的数字点击记录时达到一个:4 && ! 3如果为false,3 && ! 2为false,2 && ! 1为false,但1 && ! 0为真。由于没有执行块,awk只打印此记录,这是您想要的值。

更强大的工作可能是使用xpath来查找脚本,然后使用一些json解析器或类似的来获取值。

答案 2 :(得分:1)

awk救援!

如果必须,你可以使用这种hacky方式使用智能计数器和手工制作的分隔符。设置RS而不是FS传输循环通过字段到awk本身。多字符RS不适用于所有awks(gawk支持它)。

$ awk -v RS='[:,[]' '$0=="\"pm25\""{c=4} c&&!--c' file                 
59

$ awk -v RS='[:,[]' '$0=="\"pm10\""{c=4} c&&!--c' file   
15

答案 3 :(得分:1)

如果您不想或不能使用第三方实用程序jq进行JSON解析,我建议使用sed而不是awk,因为{ {1}}不适合基于 field 的数据解析。

awk

以上内容适用于GNU和BSD / OSX $ sed -E 's/^.*"pm25"[^[]+\[([0-9]+).+"pm10"[^[]+\[([0-9]+).*$/\1 \2/' <<< "$AQIDATA" 59 15

将结果读入变量:

sed

注意我是如何选择小写变量名的,因为最好避免shell编程中的所有大写变量,以免与特殊的shell和环境变量冲突。

如果您不能依赖源字符串中值的顺序,请使用两个单独的read pm25 pm10 < \ <(sed -E 's/^.*"pm25"[^[]+\[([0-9]+).+"pm10"[^[]+\[([0-9]+).*$/\1 \2/' <<< "$AQIDATA") 命令:

sed