我试图解析从curl请求返回的JSON,就像这样:
curl 'http://twitter.com/users/username.json' |
sed -e 's/[{}]/''/g' |
awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'
上面将JSON划分为多个字段,例如:
% ...
"geo_enabled":false
"friends_count":245
"profile_text_color":"000000"
"status":"in_reply_to_screen_name":null
"source":"web"
"truncated":false
"text":"My status"
"favorited":false
% ...
我如何打印一个特定的字段(由-v k=文本表示)?
我需要在Bash中一些简短的东西,可以在没有依赖的情况下运行,而不是香草Linux LSB和Mac OS的Python 2.7和3,并处理错误,例如,将报告JSON解析错误和丢失的属性错误,而不会抛出Python异常:
json-extract () {
if [[ "$1" == "" || "$1" == "-h" || "$1" == "-?" || "$1" == "--help" ]] ; then
echo 'Extract top level property value from json document'
echo ' Usage: json-extract <property> [ <file-path> ]'
echo ' Example 1: json-extract status /tmp/response.json'
echo ' Example 2: echo $JSON_STRING | json-extract status'
echo ' Status codes: 0 - success, 1 - json parse error, 2 - property missing'
else
python -c $'import sys, json;\ntry: obj = json.load(open(sys.argv[2])); \nexcept: sys.exit(1)\ntry: print(obj[sys.argv[1]])\nexcept: sys.exit(2)' "$1" "${2:-/dev/stdin}"
fi
}
如果有人只想从简单的JSON对象中提取值,而不需要嵌套结构,那么甚至不需要离开Bash就可以使用正则表达式。
下面是我使用基于JSON标准的bash正则表达式定义的函数:
function json_extract() {
local key=$1
local json=$2
local string_regex='"([^"\]|\\.)*"'
local number_regex='-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?'
local value_regex="${string_regex}|${number_regex}|true|false|null"
local pair_regex="\"${key}\"[[:space:]]*:[[:space:]]*(${value_regex})"
if [[ ${json} =~ ${pair_regex} ]]; then
echo $(sed 's/^"\|"$//g' <<< "${BASH_REMATCH[1]}")
else
return 1
fi
}
注意:对象和数组不支持作为值,但支持标准中定义的所有其他值类型。另外,只要具有完全相同的键名,无论对在JSON文档中有多深,都将匹配。
以OP为例:
$ json_extract text "$(curl 'http://twitter.com/users/username.json')"
My status
$ json_extract friends_count "$(curl 'http://twitter.com/users/username.json')"
245
这里我不能用任何答案。jq、shell数组、声明、grep -P、后视、前视、Python、Perl、Ruby甚至Bash都不可用。
剩下的答案都不太管用。JavaScript听起来很熟悉,但罐头上写的是Nescaffe——所以也不行:)即使有,对于我的简单需求——它们也会过度消耗和缓慢。
然而,对我来说,从我的调制解调器的JSON格式的回复中获得许多变量是极其重要的。我在Bourne shell (sh)做它与一个非常修剪下来的BusyBox在我的路由器!单独使用AWK没有任何问题:只需设置分隔符并读取数据。对于单个变量,这就是全部!
awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "login") {print $4} }' test.json
还记得我没有数组吗?我必须在AWK解析数据中分配给我在shell脚本中需要的11个变量。我所到之处,都有人说这是不可能完成的任务。这也没有问题。
我的解决办法很简单。这段代码将:
parse .json file from the question (actually, I have borrowed a working data sample from the most upvoted answer) and picked out the quoted data, plus
create shell variables from within the awk assigning free named shell variable names.
eval $( curl -s 'https://api.github.com/users/lambda' |
awk ' BEGIN { FS="""; RS="," };
{
if ($2 == "login") { print "Login=""$4""" }
if ($2 == "name") { print "Name=""$4""" }
if ($2 == "updated_at") { print "Updated=""$4""" }
}' )
echo "$Login, $Name, $Updated"
里面的空白没有任何问题。在我的使用中,相同的命令解析一个很长的单行输出。由于使用eval,此解决方案仅适用于可信数据。
调整它以提取未引用的数据很简单。对于大量变量,可以使用else if实现边际速度增益。缺乏数组显然意味着:没有额外的操作就没有多个记录。但是在数组可用的情况下,调整这个解决方案是一项简单的任务。
@maikel的sed回答几乎是有效的(但我不能评论它)。对于我的格式化好的数据-它工作。这里使用的例子没有太多(缺少引号)。它很复杂,很难修改。另外,我不喜欢进行11次调用来提取11个变量。为什么?我计时100循环提取9个变量:sed函数花了48.99秒,我的解决方案花了0.91秒!不公平?只提取9个变量:0.51秒vs. 0.02秒。