使用Unix工具解析JSON


877

我正在尝试解析从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=text)?


5
Erm不是很好的json解析btw ...字符串中的转义字符怎么办...等在SO上是否有对此的python答案(甚至是perl答案...)?
martinr

51
每当有人说“问题X可以用其他语言Y轻松解决”时,代码就是“我的工具箱里只有一块钉子……为什么还要打扰其他东西?”
BryanH

22
@BryanH:除了有时,Y语言可以更灵活地解决特定的问题X,而不论建议Y的人知道多少种语言。
jfs

14
有点迟了,但是事情就这样了。grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' package.json。这可以轻松且仅通过grep解决任务,并且完美适用于简单的JSON。对于复杂的JSON,您应该使用适当的解析器。
diosney

2
@auser,您可以将标题中的“ with sed and awk”更改为“ with UNIX tools”吗?
查尔斯·达菲

Answers:


1125

有许多专门用于从命令行操作JSON的工具,它们比使用Awk进行操作要容易和可靠得多,例如jq

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

您还可以使用系统上可能已经安装的工具(例如使用json模块的 Python)来执行此操作,从而避免任何额外的依赖关系,同时仍然可以使用适当的JSON解析器。以下假设您要使用UTF-8,原始JSON应该用UTF-8编码,这也是大多数现代终端所使用的:

Python 3:

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

Python 2:

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

历史笔记

该答案最初推荐jsawk,它应该仍然有效,但是使用起来要麻烦一些jq,并且取决于安装的独立JavaScript解释器(比Python解释器少见),因此上述答案可能更可取:

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

这个答案最初也使用了问题中的Twitter API,但是该API不再有效,因此很难复制示例进行测试,并且新的Twitter API需要API密钥,因此我改用了GitHub API无需API密钥即可轻松使用。原始问题的第一个答案是:

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

7
@thrau +1。jq它在存储库中可用,并且超级易于使用,因此它比jsawk好得多。我测试了几分钟,jq赢得了这场战斗
SzymonSadło16年

1
请注意,在Python 2中,如果将输出管道输送到另一个命令,则该print语句将始终编码为ASCII,因为您在管道中使用Python。PYTHONIOENCODING=<desired codec>在命令中插入以设置适合您终端的其他输出编码。在Python 3中,在这种情况下,默认值为UTF-8(使用print() 函数)。
马丁·彼得斯

1
使用brew install
Andy Fraley '18

1
curl -s等价于curl --silent,而jq -r表示jq --raw-output即没有字符串引号。
Serge Stroobandt

python -c“导入请求; r = requests.get(' api.github.com/users/lambda '); 打印 r.json()['name'];” 。最简单!
NotTooTechy

276

为了快速提取特定键的值,我个人喜欢使用“ grep -o”,它仅返回正则表达式的匹配项。例如,要从推文中获取“文本”字段,例如:

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

这个正则表达式比您想象的要强大。例如,它可以很好地处理带有嵌入式逗号和转义引号的字符串。我认为,如果再多做一点工作,就可以保证实际上是可以提取值的,如果它是原子的。(如果它具有嵌套,那么正则表达式当然不能做到这一点。)

为了进一步清理(尽管保留了字符串的原始转义),您可以使用:| perl -pe 's/"text"://; s/^"//; s/",$//'。(我为此进行了分析。)

对于所有坚持认为应该使用真正的JSON解析器的仇恨者-是的,这对于正确性至关重要,但是

  1. 要进行真正快速的分析,例如对值进行计数以检查数据清除错误或对数据有一般的了解,在命令行中执行某些操作会更快。打开编辑器来编写脚本会让人分心。
  2. grep -o比Python标准json库快几个数量级,至少在进行tweet时(每个〜2 KB)。我不确定这是否只是因为json速度慢(我应该和yajl比较);但是原则上,正则表达式应该更快,因为它是有限状态并且更加可优化,而不是必须支持递归的解析器,在这种情况下,它会花很多CPU来为不需要的结构树。(如果有人编写了一个进行了正确(深度受限)JSON解析的有限状态换能器,那就太好了!与此同时,我们还有“ grep -o”。)

为了编写可维护的代码,我始终使用真正的解析库。我还没有尝试过jsawk,但是如果运行良好,那将解决第一个问题。

最后一个更棘手的解决方案:我编写了一个脚本,该脚本使用Python json并将所需的键提取到制表符分隔的列中;然后我遍历一个包装器,awk该包装器允许对列进行命名访问。 在这里:json2tsv和tsvawk脚本。因此,对于此示例,它将是:

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

这种方法不能解决#2问题,它比单个Python脚本的效率低,并且有点脆弱:它强制对字符串值中的换行符和制表符进行规范化,以与awk的字段/记录分隔的世界视图很好地配合。但这确实让您停留在命令行上,比的正确性更高grep -o


11
您忘记了整数值。grep -Po '"text":(\d*?,|.*?[^\\]",)'
罗伯特

3
罗伯特:对,我的正则表达式只为该字段的字符串值编写。如您所说,可以添加整数。如果要使用所有类型,则必须做更多的工作:布尔值,null。数组和对象需要更多的工作;在标准正则表达式下,只能进行深度限制。
布伦丹·奥康纳

9
1. jq .name在命令行上工作,不需要“打开编辑器来编写脚本”。2. regex产生错误结果的速度并不重要
jfs

6
如果只需要这些值,则可以使用awk。| grep -Po '"text":.*?[^\\]",'|awk -F':' '{print $2}'
JeffCharter

34
似乎在OSX -P上缺少该选项。我在OSX 10.11.5上进行了测试,grep --version结果为grep (BSD grep) 2.5.1-FreeBSD。我在OSX上使用了“扩展的正则表达式”选项。上面的命令是grep -Eo '"text":.*?[^\\]",' tweets.json
詹斯

174

基于此处的一些建议(特别是注释中的建议)建议使用Python,我很失望地没有找到一个示例。

因此,这是一个从某些JSON数据中获取单个值的衬里。它假定您正在(从某处)管道传输数据,因此在脚本上下文中应该很有用。

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

我在下面增强了此答案,以使用bash函数:curl'some_api'| getJsonVal '钥匙'
乔Heyming

pythonpygithub.com/russell91/pythonpy几乎总是比更好的替代方法python -c,尽管它确实必须与pip一起安装。只需将json传递给py --ji -x 'x[0]["hostname"]'。如果您不想使用内置的json_input支持,仍然可以那些自动导入为py 'json.loads(sys.stdin)[0]["hostname"]'
RussellStewart

2
谢谢!为了更快速,更脏地进行JSON解析,我将其包装到一个bash函数中:jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); print($1)"; }这样我可以编写:curl ...... | jsonq 'json.dumps([key["token"] for key in obj], indent=2)'&更多类似的恐怖内容...顺便说一句,obj[0]似乎没有必要,它看起来obj在默认情况下可以正常工作(?)。
akavel 2015年

谢谢。我已经使JSON比印刷更好:jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); sys.stdout.write(json.dumps($1))"; }
Adam K Dean

4
obj[0]解析时会导致错误{ "port":5555 }。卸下后效果很好[0]
Cyber​​Ed

134

跟随MartinR和Boecko的领导:

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

这将为您提供极为友好的grep输出。很方便:

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

37
当OP询问时,您将如何提取特定密钥?
2013年

2
到目前为止,最好的答案是恕我直言,无需在大多数发行版上安装其他任何东西,您可以| grep field。谢谢!
Andrea Richiardi

7
如果我没有记错的话,所有这些操作就是格式化JSON。它不允许调用者从输出中选择特定字段,如xpath解决方案或基于“ JSON指针”的内容。
Cheeso 2014年

4
我只是得到一个键值对,而不是它本身的值。
克里斯托弗

1
jq通常在安装python时未安装。另外,一旦您使用了Python,您最好还是完整地进行解析import json...
-CpILL

125

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

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

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

jq主页说它类似于sedJSON数据。


27
仅作记录,jq是一个了不起的工具。
hoss 2013年

2
同意 我无法从接受的答案中与jsawk进行比较,因为我没有使用它,但是对于本地实验(可以安装工具的情况),我强烈建议使用jq。这是一个稍微扩展一点的示例,该示例采用数组的每个元素并使用选定的数据合成一个新的JSON对象: curl -s https://api.example.com/jobs | jq '.jobs[] | {id, o: .owner.username, dateCreated, s: .status.state}'
jbyler 2014年

2
喜欢这个。重量非常轻,并且由于它使用的是普通的旧C语言,因此可以在几乎任何位置进行编译。
Benmj 2014年

1
最实用的一种:它不需要第三方库(而jsawk需要)并且易于安装(OSX:brew install jq)
lauhub 2014年

1
对于我的用例,这是最实用,最容易实现的答案。对于Ubuntu(14.04)系统,一个简单的apt-get install jq将工具添加到了我的系统中。我正在将来自AWS CLI响应的JSON输出管道传输到jq中,它非常适合将值提取到嵌套在响应中的某些键。
布兰登K

105

使用Node.js

如果系统有 安装后,可以使用-pprint和-eevaming脚本标志JSON.parse来提取所需的任何值。

一个使用JSON字符串{ "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

1
谢谢!但就我而言,它仅与-e标志一起使用node -p -e 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
Rnd_d 2013年

33
管道!curl -s https://api.github.com/users/trevorsenior | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin').toString()).name"
nicerobot 2014年

4
这是我最喜欢的解决方案;使用一种语言(javascript)解析一种自然的数据结构(JSON)。似乎是最正确的。同样-节点可能已经在系统上可用,并且您不必处理jq的二进制文件(这似乎是另一个正确的选择)。
Eliran Malka'3

这是bash脚本函数:#jsonv获取特定属性的json对象值#第一个参数是json文档#第二个参数是应返回值的属性get_json_attribute_value(){node -pe'JSON.parse(process。 argv [1])[process.argv [2]]'“ $ 1”“ $ 2”}
Youness

6
以下可与Node.js 10配合使用:cat package.json | node -pe 'JSON.parse(fs.readFileSync(0)).version'
Ilya Boyandin,

100

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

像这样:

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

6
请原谅我想提出一个很好的答复...:我将更加努力。党派关系不仅需要编写awk脚本来摆脱它,还需要更多!
martinr

9
为什么在该oneliner解决方案中使用obj变量?它是无用的,并且根本不存储吗?你写更少的使用json.load(sys.stdin)['"key']"为例,如:curl -sL httpbin.org/ip | python -c "import json,sys; print json.load(sys.stdin)['origin']"
m3nda

64

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

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'

我认为其他人已经发出了足够的警报。我将用手机待命给救护车打电话。准备好后开火。


10
这样疯狂的谎言,这样说的:stackoverflow.com/questions/1732348/...
暂停,直至另行通知。

3
我已经阅读了所有答案,并且这个答案非常适合我,没有任何额外的依赖性。+1
eth0

那就是我想要的。唯一的更正-提供的sed命令删除引号对我不起作用,我改用sed's /“ //
g'– AlexG

44

在Python中使用Bash

在.bash_rc文件中创建一个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 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确保至少输入1,而-t 0确保从管道重定向。

关于此实现的好处是,您可以访问嵌套的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"
}

不具有b​​ash功能的curl http://foo | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["environment"][0]["name"]'
单线

1
sys.stdout.write()如果你想让它与两个蟒蛇2和3的工作
每约翰森

我认为它应该更改为system.stdout.write(obj $ 1)。这样,您可以说:getJsonVal“ ['environment'] ['name']”,就像@Cheeso的示例
Joe Heyming 2014年

1
@Narek在这种情况下,它看起来像这样:functiongetJsonVal() { py -x "json.dumps(json.loads(x)$1, sort_keys=True, indent=4)"; }
Joe Heyming

30

TickTick是用bash(<250行代码)编写的JSON解析器

这是作者文章的摘录,想像一下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

2
作为此处唯一可靠的纯bash答案,这值得更多投票。
艾德·兰德尔

有什么办法可以再次将此人变量打印到json字符串中?那将非常有用
Thomas Fournet

最后一个不推荐使用Python或其他残酷方法的答案...谢谢!
Akito

21

使用PHP CLI解析JSON

可以说是题外话,但是由于优先权占据主导地位,这个问题在不提及我们可信赖和忠诚的PHP的情况下仍然是不完整的,对吗?

使用相同的示例JSON,但让我们将其分配给变量以减少模糊性。

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

现在,为了使用PHP,使用file_get_contentsphp:// stdin流包装器。

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

或使用fgets指出并在CLI常量STDIN处已打开的流。

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

欢乐!


你甚至可以使用$argn代替fgets(STDIN)
IcanDivideBy0

糟糕,$argn仅当JSON内容在一行上时,才使用-E或-R标志...
IcanDivideBy0 2013年

21

本机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字符串包含多个电子邮件密钥,则解析器将输出john@doe.com“” john@doe.com
rtc11 '16

如果电子邮件中有破折号,例如jean-pierre@email.com,则不起作用
alexmngn

13

使用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]);"

3
这是我的最爱;)顺便说一句,您可以使用ruby -rjson将其缩写为需要库
lucapette 2011年

请注意,;在Ruby 中,final 不是必需的(仅用于将通常在不同行中的语句连接成一行)。
扎克·莫里斯

11

不幸的是,使用投票最高的答案会grep返回在我的方案中不起作用的完全匹配项,但是如果您知道JSON格式将保持不变,则可以使用lookbehindlookahead来仅提取所需的值。

# 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

您实际上从未真正知道 JSON字典中元素的顺序。根据定义,它们是无序的。这恰恰是滚动自己的JSON解析器注定要失败的根本原因之一。
Tripleee

10

如果有人只想从简单的JSON对象中提取值而不需要嵌套结构,则可以使用正则表达式而无需担心。

这是我使用基于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

Helder Pereira是否可以使用此函数提取嵌套的属性值?
vsbehere

8

有一种更简单的方法来从json字符串获取属性。以package.json文件为例,请尝试以下操作:

#!/usr/bin/env bash
my_val="$(json=$(<package.json) node -pe "JSON.parse(process.env.json)['version']")"

我们之所以使用,process.env是因为这会将文件的内容作为字符串获取到node.js中,而没有恶意内容转义其引号并将其解析为代码的风险。


使用字符串串联将值替换为解析为代码的字符串后,就可以运行任意的node.js代码,这意味着将非互联网内容与随机内容一起使用绝对不安全。有一个安全/最佳实践方法来解析JavaScript中的JSON的原因,不仅仅是评估它。
查尔斯·达菲

@CharlesDuffy不确定我是否遵循,但是JSON.parse调用应该更安全,因为require()实际上可以运行外部代码,而JSON.parse不能。
亚历山大·米尔斯

如果您的字符串实际上以绕过解析器的方式注入到JSON运行时中,则是正确的。我看不到这里的代码可靠地做到了。从环境变量中提取它,JSON.parse()并将其传递给,是的,您无疑是安全的……但是,在这里,JSON运行时正在接收带有(可信)代码的带内(不可信)内容。
查尔斯·达菲

…类似地,如果您的代码从文件中读取JSON 作为字符串并将该字符串传递给JSON.parse(),那么您也很安全,但这在这里也没有发生。
查尔斯·达菲

1
...啊,哎呀,不妨马上进入“方法”。问题是您要将要传递给的shell变量替换JSON.parse()为code。您假设放置文字反引号将保留内容的原义,但这是一个完全不安全的假设,因为文字反引号可以存在于文件内容中(并因此存在于变量中),从而可以终止引用并在未引用的上下文中输入值作为代码执行。
查尔斯·达菲

7

既然Powershell是跨平台的,我想我应该把它扔出去,因为我发现它相当直观而且非常简单。

curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json 

ConvertFrom-Json将JSON转换为Powershell自定义对象,因此您可以轻松地从那时开始使用这些属性。例如,如果仅需要'id'属性,则只需执行以下操作:

curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json | select -ExpandProperty id

如果您想从Bash中调用整个过程,则必须这样调用它:

powershell 'curl -s "https://api.github.com/users/lambda" | ConvertFrom-Json'

当然,有一种纯Powershell的方法可以避免卷曲,这是:

Invoke-WebRequest 'https://api.github.com/users/lambda' | select -ExpandProperty Content | ConvertFrom-Json

最后,还有“ ConvertTo-Json”,它可以轻松地将自定义对象转换为JSON。这是一个例子:

(New-Object PsObject -Property @{ Name = "Tester"; SomeList = @('one','two','three')}) | ConvertTo-Json

这样会产生很好的JSON:

{
"Name":  "Tester",
"SomeList":  [
                 "one",
                 "two",
                 "three"
             ]

}

诚然,在Unix上使用Windows Shell有点牺牲,但是Powershell在某些方面确实很出色,而解析JSON和XML就是其中的几个。这是跨平台版本的GitHub页面https://github.com/PowerShell/PowerShell


由于您正在推广Microsoft的新战略以开放其工具的源代码并合并开放源代码的外部工具,因此予以支持。对我们的世界来说这是一件好事。
亚历克斯(Alex)

我曾经不喜欢PowerShell,但是我必须承认对JSON的处理非常不错。
MartinThé

6

也有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'

1
或更简单的是:(xidel -s https://api.github.com/users/lambda -e 'name'-e '$json/name',或-e '($json).name')。
Reino

6

我不能在这里使用任何答案。没有可用的jq,没有shell数组,没有声明,没有grep -P,没有后向和超前,没有Python,没有Perl,没有Ruby,甚至-甚至Bash ...其余答案根本无法正常工作。JavaScript听起来很熟悉,但锡说Nescaffe-所以也不行:)即使可用,就我的简单需求而言-它们也会过大且缓慢。

但是,从我的调制解调器的json格式回复中获取许多变量对我来说非常重要。我在路由器上用非常精简的BusyBox进行了处理!单独使用awk没问题:只需设置分隔符并读取数据。对于单个变量,仅此而已!

awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "login") {print $4} }' test.json

还记得我没有数组吗?我必须在awk中将解析的数据分配给shell脚本中需要的11个变量。无论我在哪里看,都被认为是不可能完成的任务。也没有问题。

我的解决方案很简单。这段代码将:1)从问题中解析.json文件(实际上,我已经从投票率最高的答案中借用了一个工作数据样本)并挑选了引用的数据,再加上2)在awk中创建shell变量,分配免费的命名shell变量名。

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对0.02秒。


5

您可以尝试这样的事情-

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

5

您可以使用jshon

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

该站点说:“速度快两倍,内存的1/6” ...然后:“ Jshon解析,读取并创建JSON。它旨在在shell内尽可能地可用,并替换由以下组件构成的脆弱的即席解析器grep / sed / awk以及由perl / python制成的重量级单行解析器。”
罗杰(Roger)

它被列为Bash中解析JSON的推荐解决方案
qodeninja

消除结果中引号的最简单方法是什么?
gMale

4

这是用awk做到的一种方法

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

4

对于更复杂的JSON解析,我建议使用python jsonpath模块(由Stefan Goessner提供)-

  1. 安装它-

sudo easy_install -U jsonpath

  1. 用它 -

实施例file.json(从http://goessner.net/articles/JsonPath) -

{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

解析(提取价格低于10的所有书名)-

$ cat file.json | python -c "import sys, json, jsonpath; print '\n'.join(jsonpath.jsonpath(json.load(sys.stdin), 'store.book[?(@.price < 10)].title'))"

将输出-

Sayings of the Century
Moby Dick

注意:上面的命令行不包括错误检查。对于带有错误检查的完整解决方案,您应该创建一个小的python脚本,并使用try-except包装代码。


美丽的成语。我什至不了解Python,但这似乎是一个强大的解决方案
Sridhar Sarnobat,2016年

我在安装jsonpath此类安装时遇到了一些麻烦jsonpath_rw,因此,如果上述方法不起作用,可以尝试以下类似操作:1)/usr/bin/python -m pip install jsonpath-rw2)cat ~/trash/file.json | /usr/bin/python -c "from jsonpath_rw import jsonpath, parse; import sys,json; jsonpath_expr = parse('store.book[0]'); out = [match.value for match in jsonpath_expr.find(json.load(sys.stdin))]; print out;"(我使用了python二进制文件的完整路径,因为我在使用多个python时遇到了一些问题已安装)。
Sridhar Sarnobat '16

4

如果您有php

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

例如:
我们有为json提供国家iso代码的资源:http : //country.io/iso3.json,我们可以在带有curl的shell中轻松看到它:

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

但它看起来不太方便,也不可读,更好地解析json并查看可读结构:

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',
  ...

如果您有嵌套数组,则此输出将看起来更好...

希望这会有所帮助...


4

还有一个非常简单但功能强大的JSON CLI处理工具fx - https://github.com/antonmedv/fx

Bash终端中的JSON格式示例

例子

使用匿名功能:

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

如果您不传递匿名函数param => ...,则代码将自动转换为匿名函数。您可以通过以下关键字访问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。不需要学习新的语法。


更新2018-11-06

fx现在具有交互模式(

https://github.com/antonmedv/fx


7
如果您要推广自己的创作,则需要明确说明。请参阅如何成为垃圾邮件发送者。
八点三人

4

这又是一个bashpython混合答案。我发布此答案是因为我想处理更复杂的JSON输出,但是却减少了bash应用程序的复杂性。我想在http://www.arcgis.com/sharing/rest/info?f=json中破解以下JSON对象bash

{
  "owningSystemUrl": "http://www.arcgis.com",
  "authInfo": {
    "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
    "isTokenBasedSecurity": true
  }
}

在下面的示例中,我创建了自己的实现jqunquote利用python。您会注意到,一旦将python对象从jsonpython字典导入,就可以使用python语法导航字典。要浏览以上内容,语法为:

  • data
  • data[ "authInfo" ]
  • data[ "authInfo" ][ "tokenServicesUrl" ]

通过在bash中使用魔术,我们省略data并且仅在数据右侧提供python文本,即

  • jq
  • jq '[ "authInfo" ]'
  • jq '[ "authInfo" ][ "tokenServicesUrl" ]'

注意,没有参数, jq充当JSON前缀。使用参数,我们可以使用python语法从字典中提取想要的任何内容,包括导航子字典和数组元素。

这是一个演示上述内容的工作示例:

jq_py() {
cat <<EOF
import json, sys
data = json.load( sys.stdin )
print( json.dumps( data$1, indent = 4 ) )
EOF
}

jq() {
  python -c "$( jq_py "$1" )"
}

unquote_py() {
cat <<EOF
import json,sys
print( json.load( sys.stdin ) )
EOF
}

unquote() {
  python -c "$( unquote_py )"
}

curl http://www.arcgis.com/sharing/rest/info?f=json | tee arcgis.json
# {"owningSystemUrl":"https://www.arcgis.com","authInfo":{"tokenServicesUrl":"https://www.arcgis.com/sharing/rest/generateToken","isTokenBasedSecurity":true}}

cat arcgis.json | jq
# {
#     "owningSystemUrl": "https://www.arcgis.com",
#     "authInfo": {
#         "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
#         "isTokenBasedSecurity": true
#     }
# }

cat arcgis.json | jq '[ "authInfo" ]'
# {
#     "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
#     "isTokenBasedSecurity": true
# }

cat arcgis.json | jq '[ "authInfo" ][ "tokenServicesUrl" ]'
# "https://www.arcgis.com/sharing/rest/generateToken"

cat arcgis.json | jq '[ "authInfo" ][ "tokenServicesUrl" ]' | unquote
# https://www.arcgis.com/sharing/rest/generateToken

3

我已经做到了,“解析”一个特定值的json响应,如下所示:

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

显然,这里的$ url将是Twitter的URL,而$ var将是“文本”以获取该var的响应。

确实,我认为我在OP中唯一遗漏的是grep代表他要查找的特定变量所在的行。Awk抓住该行的第二项,然后用sed删除引号。

一个比我聪明的人可能会用awk或grep进行整个思考。

现在,您只需使用sed就可以完成所有操作:

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

因此,没有awk,没有grep ...我不知道为什么我以前没有想到这一点。嗯...


实际上,使用sed可以做到
tonybaldwin 2012年

1
grep | awk | sedsed | sed | sed管道是浪费的反模式。您的最后一个示例可以很容易地改写,curl "$url" | sed '/text/!d;s/\"text\"://g;s/\"//g;s/\ //g'但是像其他人指出的那样,这是容易出错的方法,一开始就不建议这样做。
Tripleee 2014年

我不得不使用grep -oPz'name \“:\”。*?\“'curloutput | sed's / name \”:/ \ n /
g'– Ferroao

3

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

例如,考虑一个工具jsonlookup,这样,如果我说jsonlookup access token id它将返回属性访问中定义的属性令牌中定义的属性id从stdin,该stdin大概是JSON数据。如果该属性不存在,则该工具不返回任何内容(退出状态1)。如果解析失败,请退出状态2并向stderr发送消息。如果查找成功,该工具将打印属性的值。

创建了用于提取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

3

使用python的两条线。如果您正在编写一个.sh文件,并且您不想依赖另一个.py文件,则该方法特别有效。它还利用了pipe的用法|echo "{\"field\": \"value\"}"可以用任何将json打印到标准输出的东西来代替。

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

问题不是要寻找Python解决方案。也请参阅评论。
Andrew Barber

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.