我试图解析从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=文本表示)?


当前回答

在shell脚本中解析JSON非常痛苦。使用更合适的语言,创建一个工具,以与shell脚本约定一致的方式提取JSON属性。您可以使用您的新工具来解决当前的shell脚本问题,然后将其添加到您的工具包中以备将来使用。

例如,考虑一个jsonlookup工具,如果我说jsonlookup访问令牌id,它将返回在来自标准输入的属性访问中定义的属性令牌中定义的属性id,这些属性令牌可能是JSON数据。如果该属性不存在,该工具将不返回任何内容(退出状态1)。如果解析失败,则退出状态2并返回标准错误消息。如果查找成功,该工具将打印属性的值。

创建了一个用于精确提取JSON值的Unix工具后,您可以轻松地在shell脚本中使用它:

access_token=$(curl <some horrible crap> | jsonlookup access token id)

任何语言都可以实现jsonlookup。下面是一个相当简洁的Python版本:

#!/usr/bin/python

import sys
import json

try: rep = json.loads(sys.stdin.read())
except:
    sys.stderr.write(sys.argv[0] + ": unable to parse JSON from stdin\n")
    sys.exit(2)
for key in sys.argv[1:]:
    if key not in rep:
        sys.exit(1)
    rep = rep[key]
print rep

其他回答

我已经这样做了,为一个特定的值“解析”JSON响应,如下所示:

curl $url | grep $var | awk '{print $2}' | sed s/\"//g

显然,这里的$url将是Twitter url, $var将是“text”,以获取该变量的响应。

实际上,我认为我所做的OP所遗漏的唯一一件事是grep,用于他所寻找的特定变量的行。AWK获取行上的第二项,并使用sed删除引号。

比我聪明的人可能会用AWK或grep来做整个思考。

现在,你可以用sed完成这一切:

curl $url | sed '/text/!d' | sed s/\"text\"://g | sed s/\"//g | sed s/\ //g

因此,没有AWK,没有grep…我不知道为什么我以前没想到。嗯…

还有一个非常简单但功能强大的JSON CLI处理工具fx。

例子

使用匿名函数:

echo '{"key": "value"}' | fx "x => x.key"

输出:

value

如果你不传递匿名函数参数→…,代码将自动转换为匿名函数。你可以通过这个关键字访问JSON:

$ echo '[1,2,3]' | fx "this.map(x => x * 2)"
[2, 4, 6]

或者也可以使用点语法:

echo '{"items": {"one": 1}}' | fx .items.one

输出:

1

你可以传递任意数量的匿名函数来减少JSON:

echo '{"items": ["one", "two"]}' | fx "this.items" "this[1]"

输出:

two

您可以使用扩展操作符更新现有JSON:

echo '{"count": 0}' | fx "{...this, count: 1}"

输出:

{"count": 1}

只是简单的JavaScript。没有必要学习新的语法。


fx的后期版本有一个互动模式!-

有一个有趣的工具在现有的答案中还没有涉及到,那就是使用用Go编写的gron,它的口号是Make JSON可greppable!这正是它所做的。

所以从本质上讲,gron将JSON分解为离散的赋值,查看它的绝对“路径”。与jq等其他工具相比,它的主要优点是允许在不知道要搜索的记录是如何嵌套的情况下搜索值,而不会破坏原始的JSON结构

例如,我想从下面的链接搜索'twitter_username'字段,我只是这样做

% gron 'https://api.github.com/users/lambda' | fgrep 'twitter_username'
json.twitter_username = "unlambda";
% gron 'https://api.github.com/users/lambda' | fgrep 'twitter_username' | gron -u
{
  "twitter_username": "unlambda"
}

就这么简单。请注意gron -u (ungron的缩写)如何从搜索路径重新构造JSON。使用fgrep只是为了将搜索过滤到所需的路径,而不是让搜索表达式作为正则表达式计算,而是作为固定字符串(本质上是grep -F)

另一个搜索字符串以查看记录在嵌套结构中的位置的示例

% echo '{"foo":{"bar":{"zoo":{"moo":"fine"}}}}' | gron | fgrep "fine"
json.foo.bar.zoo.moo = "fine";

它还通过-s命令行标志支持JSON流,在这里您可以连续地对输入流进行gron以获得匹配的记录。此外,gron具有零运行时依赖性。你可以下载Linux、Mac、Windows或FreeBSD的二进制文件并运行它。

更多的用法示例和行程可以在官方Github页面-高级用法中找到

至于为什么可以使用gron而不是其他JSON解析工具,请参阅项目页面的作者注释。

为什么我不应该直接使用jq?

Jq非常棒,比gron强大得多,但这种强大带来了复杂性。Gron的目标是使您更容易使用您已经知道的工具,如grep和sed。

有XML文件的人可能想看看我的Xidel。它是一个命令行界面,无依赖的JSONiq处理器。(也就是说,它还支持XQuery进行XML或JSON处理。)

问题中的例子是:

 xidel -e 'json("http://twitter.com/users/username.json")("name")'

或者用我自己的非标准扩展语法:

 xidel -e 'json("http://twitter.com/users/username.json").name'

这是一个很好的参考资料。在这种情况下:

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++) { where = match(a[i], /\"text\"/); if(where) {print a[i]} }  }'