用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器的人来说,这是正确的,但是
- 为了做一个非常快速的分析,比如计算值来检查数据清理错误,或者对数据有一个总体的了解,在命令行上敲出一些东西会更快。 打开编辑器来编写脚本是让人分心的事情。
-
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
文件中,你可以复制和粘贴throw
, parse_array
, parse_object
, parse_value
和parse
命令到脚本中。 将您的脚本更改为:
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 ' '
怎么运行的:
-
curl
得到一个json文件,然后将其转移到grep
。 -
grep
“标记化”json(它打印出一个string,一个数字,或者一个字符等),然后将其传递给第二个grep
其工作是消耗空格。 这不适用于你,如果你不从JSON.sh复制function。 - 如果从JSON.sh复制函数,则
parse
函数recursion地输出值的path,然后是标签,然后是值。 如果你不是 ,那么JSON.sh标记,吃掉空格并在内部调用parse
函数。 然后将它们的输出pipe道grep
。 -
grep
search你想要的对的键和值的列表。 它search关键字,并用键和值打印出行。 密钥是如何格式化的:[aKey]其中aKey
是密钥。 如果你的json是嵌套的,用逗号隔开。 如果您的值在数组中,请使用数组索引(从零开始)获取该值。 将键/值行传送到cut
(使用-e“[aKey]”的多个实例一次获取多个值)。 -
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格式将保持不变,则可以使用lookbehead和lookahead来提取所需的值。
# 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读取得很好。