用Unix工具parsingJSON

我试图parsing从curl请求返回的JSON,如下所示:

curl 'http://twitter.com/users/username.json' | sed -e 's/[{}]/''/g' | awk -vk="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 % ... 

如何打印特定字段(用-vk=text )?

有许多工具是专门为了从命令行操纵JSON而devise的,而且比使用Awk更容易,更可靠,比如jq

 curl -s 'https://api.github.com/users/lambda' | jq -r '.name' 

您也可以使用可能已经安装在您的系统上的工具(比如使用json模块的 Python)来做到这一点,因此避免任何额外的依赖关系,同时仍然有合适的JSONparsing器的好处。 下面假设你想要使用原始JSON应该被编码的UTF-8,并且也是大多数现代terminal使用的:

Python 2:

 export PYTHONIOENCODING=utf8 curl -s 'https://api.github.com/users/lambda' | \ python -c "import sys, json; print json.load(sys.stdin)['name']" 

Python 3:

 curl -s 'https://api.github.com/users/lambda' | \ python3 -c "import sys, json; print(json.load(sys.stdin)['name'])" 

历史笔记

这个答案最初build议jsawk ,它仍然可以工作,但比jq使用起来更麻烦一些,并且依赖于一个独立的JavaScript解释器被安装,这比Python解释器不太常见,所以上面的答案可能是更好的:

 curl -s 'https://api.github.com/users/lambda' | jsawk -a 'return this.name' 

这个答案最初也是使用了Twitter API,但是这个API不再适用,很难复制这些例子来testing,而且新的Twitter API需要API密钥,所以我已经转向使用GitHub API无需API密钥就可以轻松使用。 原始问题的第一个答案是:

 curl 'http://twitter.com/users/username.json' | jq -r '.text' 

要快速提取特定键的值,我个人喜欢使用“grep -o”,它只返回正则expression式的匹配。 例如,要从推文中获取“文本”字段,如下所示:

 grep -Po '"text":.*?[^\\]",' tweets.json 

这个正则expression式比你想像的更强大。 例如,它可以很好地处理内嵌逗号的string和内部的引号。 我认为只要多做一点工作,就可以使一个实际上保证提取价值的东西,如果它是primefaces的。 (如果嵌套,那么正则expression式不能这样做)

并进一步清理(尽pipe保持string的原始逃逸),你可以使用像: | perl -pe 's/"text"://; s/^"//; s/",$//' | perl -pe 's/"text"://; s/^"//; s/",$//' | perl -pe 's/"text"://; s/^"//; s/",$//' 。 (我这样做了这个分析 。)

对于所有坚持使用真正的JSONparsing器的人来说,这是正确的,但是

  1. 为了做一个非常快速的分析,比如计算值来检查数据清理错误,或者对数据有一个总体的了解,在命令行上敲出一些东西会更快。 打开编辑器来编写脚本是让人分心的事情。
  2. grep -o比Python标准json库要快几个数量级,至less在推文(每个大约2KB)时这样做。 我不确定这是不是因为json比较慢(我应该和yajl比较一下); 但是原则上,正则expression式应该是更快的,因为它是有限状态并且更加可优化,而不是必须支持recursion的parsing器,在这种情况下,花费大量的CPU构build树来构build您不关心的树。 (如果有人写了一个有限状态转换器,做了适当的(深度限制的)JSONparsing,那太棒了!同时我们有“grep -o”。)

要编写可维护的代码,我总是使用一个真正的parsing库。 我没有尝试过jsawk ,但是如果它运行的很好,那将会解决第一点。

最后一个更奇怪的解决scheme是:我编写了一个使用Python json的脚本,并将所需的键提取到制表符分隔的列中; 然后我通过awk的包装器,允许命名访问列。 在这里:json2tsv和tsvawk脚本 。 所以这个例子就是:

 json2tsv id text < tweets.json | tsvawk '{print "tweet " $id " is: " $text}' 

这种方法没有解决#2的问题,比单个Python脚本效率低,而且有点脆弱:它强制对string值中的换行符和制表符进行标准化,以便与awk的以字段/logging分隔的世界视图相配合。 但它确实让你留在命令行,比grep -o更正确。

基于这里的一些build议(特别是在评论中)build议使用Python,我很失望没有find一个例子。

所以,这里是一个单线程来从一些JSON数据中获取单个值。 它假定你正在从(从某处)传送数据,所以在脚本上下文中应该是有用的。

 echo '{"hostname":"test","domainname":"example.com"}' | python -c 'import json,sys;obj=json.load(sys.stdin);print obj[0]["hostname"]' 

MartinR和Boecko的领导之后:

 $ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool 

这会给你一个非常友善的输出。 很方便:

 $ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool | grep my_key 

你可以下载平台的jq二进制文件并运行( chmod +x jq ):

 $ curl 'https://twitter.com/users/username.json' | ./jq -r '.name' 

它从json对象中提取"name"属性。

jq主页说这就像sed的JSON数据。

使用Python的JSON支持,而不是使用awk!

像这样的东西:

 curl -s http://twitter.com/users/username.json | \ python -c "import json,sys;obj=json.load(sys.stdin);print obj['name'];" 

使用Node.js

如果系统安装了节点 ,则可以使用-p print和-e evaulate脚本标记并使用JSON.parse来提取所需的任何值。

使用JSONstring{ "foo": "bar" }并取出“foo”的值的简单示例:

 $ node -pe 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }' bar 

因为我们可以访问cat和其他实用程序,所以我们可以将它用于文件:

 $ node -pe 'JSON.parse(process.argv[1]).foo' "$(cat foobar.json)" bar 

或者其他任何格式,例如包含JSON的URL:

 $ node -pe 'JSON.parse(process.argv[1]).name' "$(curl -s https://api.github.com/users/trevorsenior)" Trevor Senior 

你已经问过如何在脚下射击自己,我在这里提供弹药:

 curl -s 'http://twitter.com/users/username.json' | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^text/ {print $2}' 

你可以使用tr -d '{}'而不是sed 。 但是把它们完全抛弃似乎也会产生预期的效果。

如果你想剥离外部引号,通过sed 's/\(^"\|"$\)//g'

我觉得别人已经听够了。 我会用手机来叫救护车。 准备就绪

与Python一起使用Bash

在.bash_rc文件中创build一个bash函数

 function getJsonVal () { python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))"; } 

然后

 $ curl 'http://twitter.com/users/username.json' | getJsonVal "['text']" My status $ 

这是相同的function,但有错误检查。

 function getJsonVal() { if [ \( $# -ne 1 \) -o \( -t 0 \) ]; then cat <<EOF Usage: getJsonVal 'key' < /tmp/ -- or -- cat /tmp/input | getJsonVal 'key' EOF return; fi; python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))"; } 

其中$#-ne 1确保至less有1个input,并且-t 0确保您从pipe道redirect。

关于这个实现的好处是你可以访问嵌套的json值并获得json的回报! =)

例:

 $ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' | getJsonVal "['foo']['a'][1]" 2 

如果你想成为真正的幻想,你可以漂亮的打印数据:

 function getJsonVal () { python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1, sort_keys=True, indent=4))"; } $ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' | getJsonVal "['foo']" { "a": [ 1, 2, 3 ], "bar": "baz" } 

JQ

 $ curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | \ jq '.[0] | {message: .commit.message, name: .commit.committer.name}' 

用PHP CLIparsingJSON

可争论的话题,但自从优先统治这个问题仍然是不完整的,没有提到我们的信任和忠实的PHP,我说得对吗?

使用相同的示例JSON,但可以将其分配给一个variables,以减less模糊。

 $ export JSON='{"hostname":"test","domainname":"example.com"}' 

现在对于PHP来说,使用file_get_contents和php:// stdinstream包装器。

 $ echo $JSON|php -r 'echo json_decode(file_get_contents("php://stdin"))->hostname;' 

或者像使用fgets和CLI常量STDIN中已经打开的stream那样指出。

 $ echo $JSON|php -r 'echo json_decode(fgets(STDIN))->hostname;' 

的nJoy!

不要这样做

不要使用面向行的工具来parsing序列化为文本的分层数据。 它只适用于特殊情况,会困扰你和其他人 。 如果你真的不能使用现成的jsonparsing器,用recursion下降法编写一个简单的parsing器。 这很容易,将忍受排放方面考虑化妆品(添加或删除包括换行符在内的空白)的变化。

TickTick是一个用bash编写的JSONparsing器(<250行代码)

这里是作者从他的文章嗤之以鼻, 想象一下Bash支持JSON的世界 :

 #!/bin/bash . ticktick.sh `` people = { "Writers": [ "Rod Serling", "Charles Beaumont", "Richard Matheson" ], "Cast": { "Rod Serling": { "Episodes": 156 }, "Martin Landau": { "Episodes": 2 }, "William Shatner": { "Episodes": 2 } } } `` function printDirectors() { echo " The ``people.Directors.length()`` Directors are:" for director in ``people.Directors.items()``; do printf " - %s\n" ${!director} done } `` people.Directors = [ "John Brahm", "Douglas Heyes" ] `` printDirectors newDirector="Lamont Johnson" `` people.Directors.push($newDirector) `` printDirectors echo "Shifted: "``people.Directors.shift()`` printDirectors echo "Popped: "``people.Directors.pop()`` printDirectors 

本地Bash版本:也适用于反斜杠(\)和引号(“)

 function parse_json() { echo $1 | \ sed -e 's/[{}]/''/g' | \ sed -e 's/", "/'\",\"'/g' | \ sed -e 's/" ,"/'\",\"'/g' | \ sed -e 's/" , "/'\",\"'/g' | \ sed -e 's/","/'\"---SEPERATOR---\"'/g' | \ awk -F=':' -v RS='---SEPERATOR---' "\$1~/\"$2\"/ {print}" | \ sed -e "s/\"$2\"://" | \ tr -d "\n\t" | \ sed -e 's/\\"/"/g' | \ sed -e 's/\\\\/\\/g' | \ sed -e 's/^[ \t]*//g' | \ sed -e 's/^"//' -e 's/"$//' } parse_json '{"username":"john, doe","email":"john@doe.com"}' username parse_json '{"username":"john doe","email":"john@doe.com"}' email --- outputs --- john, doe johh@doe.com 

不要重新发明轮子,并从JSON创build者推荐的官方JSONparsing软件中select: http : //www.json.org/ (见底部)

使用Ruby和http://flori.github.com/json/的版本;

 $ < file.json ruby -e "require 'rubygems'; require 'json'; puts JSON.pretty_generate(JSON[STDIN.read]);" 

或者更简洁:

 $ < file.json ruby -r rubygems -r json -e "puts JSON.pretty_generate(JSON[STDIN.read]);" 

每个人似乎都低估awk。 没错,一行或两行的awk脚本是不够的。 但是在awk中编写真正的JSONparsing器并不难。 我刚刚添加一个到我awesomeough库。

几年后,我猜(对不起),但我已经创build了一个纯粹的bash脚本,支持嵌套,并可以很容易地获得价值观。

脚本的主要部分:

 curl "http://twitter.com/users/username.json" | ./JSON.sh | grep -F -e "[\"text\"] | cut -s -f 2 -d ' ' 

该脚本需要工作目录中的JSON.sh。

json格式的作者build议在Bash中使用JSON.sh (转到www.json.org并向下滚动到第二部分,并在“Bash”下列出)。

如果你想让你的脚本在一个.sh文件中,你可以复制和粘贴throwparse_arrayparse_objectparse_valueparse命令到脚本中。 将您的脚本更改为:

 curl "http://twitter.com/users/username.json" | grep -aoE '\"[^[:cntrl:]"\\]*((\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})[^[:cntrl:]"\\]*)*\"|-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?|null|false|true|[[:space:]]+$|.' --color=never | grep -vE '^[[:space:]]+' | parse | grep -F -e "[\"text\"] | cut -s -f 2 -d ' ' 

怎么运行的:

  1. curl得到一个json文件,然后将其转移到grep
  2. grep “标记化”json(它打印出一个string,一个数字,或者一个字符等),然后将其传递给第二个grep其工作是消耗空格。 这不适用于你,如果你从JSON.sh复制function。
  3. 如果从JSON.sh复制函数,则parse函数recursion地输出值的path,然后是标签,然后是值。 如果你不是 ,那么JSON.sh标记,吃掉空格并在内部调用parse函数。 然后将它们的输出pipe道grep
  4. grepsearch你想要的对的键和值的列表。 它search关键字,并用键和值打印出行。 密钥是如何格式化的:[aKey]其中aKey是密钥。 如果你的json是嵌套的,用逗号隔开。 如果您的值在数组中,请使用数组索引(从零开始)获取该值。 将键/值行传送到cut (使用-e“[aKey]”的多个实例一次获取多个值)。
  5. cut将两行中的行作为分隔符,然后仅打印该值。

这样你可以只使用1个脚本文件并使用纯bash,并且支持嵌套的json 。 请享用! (尽pipe你现在可能已经转到其他项目)。

我创build了一个专门用于命令行JSON操作的模块:

https://github.com/ddopson/underscore-cli

  • 灵活 – 用于处理JSON数据的“瑞士军刀”工具可以用作简单的漂亮打印机,也可以用作全function的JavaScript命令行
  • 强大 – 揭示了underscore.js的全部function和function(加上underscore.string)
  • 简单 – 使用类似于使用“perl -pe”编写JS单行程序很简单
  • CHAINED – 可以将多个命令调用链接在一起以创build数据处理pipe道
  • 多格式 – 丰富的input/输出格式支持 – 漂亮的打印,严格的JSON,msgpack等
  • 文档 – 每个命令都有多个示例的优秀命令行文档

select一个字段非常简单:

 cat file.json | underscore extract field.subfield.subsubfield 

默认情况下,它会用“smart-whitespace”来打印输出,它是可读的,100%严格的JSON(但你可以select其他格式的标志):

如果你有任何function请求,评论这个职位或在github中添加一个问题。 我很乐意优先考虑社区成员需要的function。

你可以使用jshon

 curl 'http://twitter.com/users/username.json' | jshon -e text 

这里有一个方法可以用awk来完成

 curl -sL 'http://twitter.com/users/username.json' | awk -F"," -vk="text" '{ gsub(/{|}/,"") for(i=1;i<=NF;i++){ if ( $i ~ k ){ print $i } } }' 

这可能被认为是不重要的,但可能会有所帮助。 这是我的Spotify URI的JSONparsing器:

 wget -qO- "http://ws.spotify.com/lookup/1/.json?uri=[spotify url goes here]" | jsawk 'return this.track.artists[0].name + " - " + this.track.name + " (" + this.track.album.name + ")"' 

当使用Spotify URI作为参数(例如$1 )时,该脚本非常有用。

jsawk @ github

你可以尝试这样的事情 –

 curl -s 'http://twitter.com/users/jaypalsingh.json' | awk -F=":" -v RS="," '$1~/"text"/ {print}' 

有人也有XML文件,可能要看看我的Xidel 。 这是一个cli,免费的JSONiq处理器。 (即它也支持XQuery的XML或JSON处理)

问题中的例子是:

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

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

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

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

例如,考虑一个工具jsonlookup ,如果我说jsonlookup access token id ,它将返回属性标记中定义的属性标识,该属性标识是从stdin(可能是JSON数据) 访问的属性标记中定义的。 如果该属性不存在,则该工具不返回任何内容(退出状态1)。 如果parsing失败,则退出状态2并将消息发送到stderr。 如果查找成功,则该工具将打印该属性的值。

为了提取JSON值的精确目的而创build了一个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 

一个使用Python的双线程。 如果你正在编写一个单独的.sh文件,并且你不想依赖另一个.py文件,那么它的工作效果特别好。 它也利用了pipe |的使用 。 echo "{\"field\": \"value\"}"可以被任何将json打印到标准输出的东西所替代。

 echo "{\"field\": \"value\"}" | python -c 'import sys, json print(json.load(sys.stdin)["field"])' 

这是pythonpy的一个很好的用例:

 curl 'http://twitter.com/users/username.json' | py 'json.load(sys.stdin)["name"]' 

如果你有php

 php -r 'var_export(json_decode(`curl http://twitter.com/users/username.json`, 1));' 

例如:
我们有资源,提供json与国家iso代码: http : //country.io/iso3.json ,我们可以很容易地看到它在一个壳上curl:

 curl http://country.io/iso3.json 

但看起来不是很方便,而且不可读,更好地parsingjson并查看可读结构:

 php -r 'var_export(json_decode(`curl http://country.io/iso3.json`, 1));' 

此代码将打印如下所示:

 array ( 'BD' => 'BGD', 'BE' => 'BEL', 'BF' => 'BFA', 'BG' => 'BGR', 'BA' => 'BIH', 'BB' => 'BRB', 'WF' => 'WLF', 'BL' => 'BLM', ... 

如果你有嵌套的数组这个输出将看起来好多了…

希望这会有帮助…

不幸的是,使用grep的顶级投票答案会返回完全匹配,但是如果您知道JSON格式将保持不变,则可以使用lookbeheadlookahead来提取所需的值。

 # echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="FooBar":")(.*?)(?=",)' he\"llo # echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="TotalPages":)(.*?)(?=,)' 33 # echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="anotherValue":)(.*?)(?=})' 100 

如何使用Rhino ? 这是一个命令行的JavaScript工具。 不幸的是,这种types的应用程序有点粗糙。 它不会从标准input读取得很好。