使用Unix工具解析JSON

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

1508630 次浏览

有许多专门设计用于从命令行操作JSON的工具,并且比使用Awk更容易和更可靠,例如#0

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

您还可以使用可能已经安装在您的系统上的工具来执行此操作,例如使用#0模块的Python,因此避免任何额外的依赖项,同时仍然具有适当的JSON解析器的好处。以下假设您想使用UTF-8,原始JSON应该编码在其中,并且大多数现代终端也使用UTF-8:

python3:

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

python2:

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

热门问题

为什么不是纯壳解决方案?

标准POSIX/单Unix规范shell是一种非常有限的语言,它不包含表示序列(列表或数组)或关联数组(在其他一些语言中也称为哈希表、映射、字典或对象)的工具。这使得在可移植的shell脚本中表示解析JSON的结果有些棘手。有做这件事的方法有点老套,但如果键或值包含某些特殊字符,其中许多可能会中断。

Bash 4及更高版本,zsh和ksh支持数组和关联数组,但这些shell并不普遍可用(由于从GPLv2更改为GPLv3,macOS在Bash 3停止更新Bash,而许多Linux系统没有开箱即用地安装zsh)。你可以编写一个在Bash 4或zsh中工作的脚本,其中一个在大多数macOS,Linux和BSD系统上都可用,但是很难编写一个适用于这种多语言脚本的sheband行。

最后,在shell中编写一个完整的JSON解析器将是一个足够重要的依赖项,您不妨使用像jq或Python这样的现有依赖项来代替。

为什么不使用awk、ses或grep?

可以使用这些工具从具有已知形状并以已知方式格式化的JSON中进行一些快速提取,例如每行一个键。在其他答案中有几个建议示例。

但是,这些工具是为基于行或基于记录的格式而设计的;它们不是为具有可能转义字符的匹配分隔符的递归解析而设计的。

因此,这些使用awk/ses/grep的快速而肮脏的解决方案可能很脆弱,如果输入格式的某些方面发生变化,例如折叠空格,或向JSON对象添加额外的嵌套级别,或字符串中的转义引号,则会中断。一个足够健壮的解决方案来处理所有JSON输入而不中断也将是相当大和复杂的,因此与在jq或Python上添加另一个依赖项没有太大区别。

我以前不得不处理由于外壳脚本中的输入解析不佳而被删除的大量客户数据,所以我从不推荐以这种方式可能脆弱的快速和肮脏的方法。如果你正在做一些一次性处理,请参阅其他答案以获得建议,但我仍然强烈建议只使用现有的测试JSON解析器。

历史笔记

这个答案最初推荐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'

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

像这样的东西:

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

macOS v12.3(Monterey)已删除#0,因此我们必须在macOS v12.3及更高版本中使用/usr/bin/python3

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

你问过如何开枪打自己的脚,我在这里提供弹药:

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'管道传输

我想其他人已经发出了足够的警报。我会用手机待命叫救护车。准备好就开火。

这里有一个方法你可以用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}}}'

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

以下Boecko的线索955668">martinr's和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

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

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

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

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

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

  1. 要进行真正快速的分析,例如计算值以检查数据清理错误或对数据有一个大致的感觉,在命令行上敲打一些东西会更快。打开编辑器编写脚本会分散注意力。
  2. grep -o比Python标准json库快几个数量级,至少在对推文(每个约2 KB)执行此操作时是这样。我不确定这是否只是因为json很慢(我应该找个时间与yajl进行比较);但原则上,正则表达式应该更快,因为它是有限状态并且更可优化,而不是必须支持递归的解析器,在这种情况下,花费大量CPU为您不关心的结构构建树。(如果有人编写了一个有限状态转换器来进行适当的(深度有限)JSON解析,那就太棒了!与此同时,我们有“grep-o”。)

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

最后一个更古怪的解决方案:我编写了一个脚本,使用Pythonjson并将您想要的键提取到制表符分隔的列中;然后我通过围绕awk的包装器进行管道传输,该包装器允许对列进行命名访问。在这里:json2tsv和tsvawk脚本。所以对于这个例子来说,它将是:

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

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

基于这里的一些建议(尤其是在评论中)建议使用Python,我很失望没有找到一个例子。

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

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

你可以试试这个-

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

更新(2020)

我对外部工具(例如Python)的最大问题是,您必须处理包管理器和依赖项才能安装它们。

但是,现在我们有了jq作为一个独立的静态工具,可以通过github发布和Webi(webinstall.dev/jq)轻松安装跨平台,我建议:

麦克,Linux:

curl -sS https://webinstall.dev/jq | bash

Windows 10:

curl.exe -A MS https://webinstall.dev/jq | powershell

备忘单https://webinstall.dev/jq

原始(2011)

滴答滴答是用bash编写的JSON解析器(少于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()``; doprintf "    - %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

您可以使用#0

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

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

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

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

真的,我认为我在做OP时唯一遗漏的是grep,用于他寻找的特定变量的行。AWK抓取行上的第二个项目,并使用ses我去掉引号。

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

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

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

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

使用PHP CLI解析JSON

这可以说是离题的,但由于优先权占主导地位,这个问题仍然不完整,没有提到我们的信任和忠诚php,我是对的吗?

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

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

现在对于PHP的优点,它使用file_get_contents流包装器。

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

或者使用fget和CLI常量STDIN处已经打开的流指出。

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

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

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

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

#0主页表示它类似于JSON数据的sed

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

问题中的例子是:

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

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

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

使用Node.js

如果系统安装了Node.js,则可以使用-p打印和-e评估脚本标志#2来提取所需的任何值。

一个使用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

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

例如,考虑一个工具jsonlookup,如果我说jsonlookup access token id,它将从标准输入返回在属性访问中定义的属性令牌中定义的属性id,这大概是JSON数据。如果属性不存在,工具不返回任何内容(退出状态1)。如果解析失败,退出状态2和一条指向标准错误的消息。如果查找成功,工具会打印属性的值。

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

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

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

#!/usr/bin/python
import sysimport 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

将Bash与Python结合使用

. bashrc文件中创建一个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 \) ]; thencat <<EOFUsage: getJsonVal 'key' < /tmp/-- or --cat /tmp/input | getJsonVal 'key'EOFreturn;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"}

对于更复杂的JSON解析,我建议使用Pythonjsonpath模块(由Stefan Goessner编写)-

  1. 安装它-

    sudo easy_install -U jsonpath
  2. 使用它-

    示例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 CenturyMoby Dick

    说明:上面的命令行不包括错误检查。对于带有错误检查的完整解决方案,您应该创建一个小型Python脚本,并用尝试除了包装代码。

这是一个很好的用例蟒蛇

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

这是使用大多数分布上可用的标准Unix工具。它也适用于反斜杠(\)和引号(")。

警告:这与jq的功能不太接近,只能用于非常简单的JSON对象。这是对原始问题的回答,并且是在您无法安装其他工具的情况下。

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"}' usernameparse_json '{"username":"john doe","email":"john@doe.com"}' email
--- outputs ---
john, doejohh@doe.com

使用Python的两行代码。如果您正在编写单个. sh文件并且不想依赖另一个. py文件,它的工作效果特别好。它还利用了管道|的使用。echo "{\"field\": \"value\"}"可以被任何将JSON文件打印到标准输出的东西替换。

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

这是一个很好的参考.在这种情况下:

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]} }  }'

如果您安装了php解释器:

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

例如:

我们有一个资源提供带有国家ISO代码http://country.io/iso3.json的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',...

如果你有嵌套数组,这个输出会看起来更好…

我用它来从ffProjs JSON输出中提取内容长度:

MOVIE_INFO=`ffprobe "path/to/movie.mp4"  -show_streams -show_format -print_format json -v quiet`MOVIE_SECONDS=`echo "$MOVIE_INFO"|grep -w \"duration\" |tail -1 | cut -d\" -f4 |cut -d \. -f 1`

它可用于从任何JSON文件中提取值:

value=`echo "$jsondata" | grep -w \"key_name\" |tail -1 | cut -d\" -f4

如果pip在系统上可用,则:

$ pip install json-query

使用示例:

$ curl -s http://0/file.json | json-query{"key":"value"}
$ curl -s http://0/file.json | json-query my.keyvalue
$ curl -s http://0/file.json | json-query my.keys.key_1key_2key_3
$ curl -s http://0/file.json | json-query my.keys.2value_2

不幸的是,使用grep的投票最多的答案返回了在我的场景中不起作用的充分匹配,但是如果你知道JSON格式将保持不变,你可以使用看后面向前看来提取所需的值。

# 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

现在PowerShell是跨平台的,我想我会把它扔出去,因为我发现它相当直观且非常简单。

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

从当前位置转换为当前位置的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的方法可以在没有curl的情况下做到这一点,这将是:

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

最后,还有//转换为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页面:PowerShell

这是另一个Bash和Python混合的答案。我发布了这个答案,因为我想处理更复杂的JSON输出,但是,降低我的bash应用程序的复杂性。我想在Bash中从http://www.arcgis.com/sharing/rest/info?f=json打开以下JSON对象:

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

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

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

通过在Bash中使用魔法,我们省略了data,只提供数据右侧的Python文本,即:

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

注意,如果没有参数,jq充当JSON美化器。使用参数,我们可以使用Python语法从字典中提取我们想要的任何东西,包括导航子字典和数组元素。

以下是Bash Python混合函数:

#!/bin/bash -xe
jq_py() {cat <<EOFimport json, sysdata = json.load( sys.stdin )print( json.dumps( data$1, indent = 4 ) )EOF}
jq() {python -c "$( jq_py "$1" )"}
unquote_py() {cat <<EOFimport json,sysprint( json.load( sys.stdin ) )EOF}
unquote() {python -c "$( unquote_py )"}

以下是Bash Python函数的示例用法:

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

如果有人只想从简单的JSON对象中提取值而不需要嵌套结构,那么甚至可以在不离开Bash的情况下使用正则表达式。

这是我使用基于JSON标准的bash正则表达式定义的函数:

function json_extract() {local key=$1local 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} ]]; thenecho $(sed 's/^"\|"$//g' <<< "${BASH_REMATCH[1]}")elsereturn 1fi}

注意:不支持对象和数组作为值,但支持标准中定义的所有其他值类型。此外,只要具有完全相同的键名,无论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

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

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

我们使用process.env,因为它将文件的内容作为字符串Node.js,而不会有恶意内容逃避引用并被解析为代码的风险。

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

Bash终端中的JSON格式示例

示例

使用匿名函数:

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有一个互动模式!-

Niet是一个工具,可帮助您直接在shell或Bash CLI中从JSON或YAML文件中提取数据。

pip install niet

考虑一个名为project.json的JSON文件,其内容如下:

{project: {meta: {name: project-sample}}

你可以像这样使用Niet:

PROJECT_NAME=$(niet project.json project.meta.name)echo ${PROJECT_NAME}

输出:

project-sample

您可以使用bashJson

它是Python的JSON模块的包装器,可以处理复杂的JSON数据。

让我们考虑文件test.json中的这个示例JSON数据

{"name":"Test tool","author":"hack4mer","supported_os":{"osx":{"foo":"bar","min_version" : 10.12,"tested_on" : [10.1,10.13]},"ubuntu":{"min_version":14.04,"tested_on" : 16.04}}}

以下命令从这个示例JSON文件中读取数据

./bashjson.sh test.json name

打印:测试工具

./bashjson.sh test.json supported_os osx foo

指纹:酒吧

./bashjson.sh test.json supported_os osx tested_on

打印:[10.1,10.13]

我不能在这里使用任何答案。jq、shell数组、声明、grep-p、look的后面、look的前面、Python、Perl、Ruby,甚至Bash,都不可用。

其余的答案根本不起作用。JavaScript听起来很熟悉,但锡说Nescaffe-所以它也是一个不可用的:)即使可用,对于我的简单需求-它们也会过度和缓慢。

然而,从调制解调器的JSON格式回复中获取许多变量对我来说非常重要。我在Bourne shellsh)中这样做,在我的路由器上非常修剪BusyBox!仅使用AWK没有任何问题:只需设置分隔符并读取数据。对于单个变量,仅此而已!

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

还记得我没有任何数组吗?我必须在AWK中将解析后的数据分配给外壳脚本中需要的11个变量。无论我在哪里看,据说这是一项不可能完成的任务。这也没问题。

我的解决方案很简单。此代码将:

  1. 从问题中解析. json文件(实际上,我从投票最多的答案中借用了一个工作数据样本)并挑选出引用的数据,加上

  2. 从awk中创建shell变量,分配自由命名的shell变量名称。

    val$(curl-s'https://api.github.com/users/lambda'|{if(2美元=="登录"){print"登录=""4美元""}if(2美元=="姓名"){print"姓名=""4美元""}if(2美元=="updated_at"){print"已更新="4美元""}}')回显“$登录,$名称,$更新”

内部空格没有任何问题。在我的使用中,相同的命令解析长单行输出。由于使用了val,此解决方案仅适用于受信任的数据。

调整它以拾取未引用的数据很简单。对于大量变量,使用否则如果可以实现边际速度增益。缺乏数组显然意味着:没有额外的摆弄就没有多个记录。但是在数组可用的地方,调整这个解决方案是一项简单的任务。

@迈克尔的se答案几乎可以工作(但我不能对此发表评论)。对于我格式良好的数据-它可以工作。这里使用的例子不是那么多(缺少引号会使它失效)。它很复杂,难以修改。另外,我不喜欢必须进行11次调用来提取11个变量。为什么?我计时100次循环提取9个变量:se函数花了48.99秒,我的解决方案花了0.91秒!不公平?只提取9个变量:0.51 vs.0.02秒。

我在Bash中需要一些简短的东西,并且可以在Python 2.7和3的vanillaLinuxLSB和Mac OS之外的依赖项下运行,并处理错误,例如报告JSON解析错误和缺少属性错误而不会产生Python异常:

json-extract () {if [[ "$1" == "" || "$1" == "-h" || "$1" == "-?" || "$1" == "--help" ]] ; thenecho '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'elsepython -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}

以下是使用POSIX shell(带local)和egrep的shell书呆子的答案:JSON.sh,4.7 KB。

这个东西有很多测试用例,所以它应该是正确的。它也是管道的。它用于Bash的包管理器,bpkg

以下是Node.js就绪环境的简单方法:

curl -L https://github.com/trentm/json/raw/master/lib/json.js > jsonchmod +x jsonecho '{"hello":{"hi":"there"}}' | ./json "hello.hi"

现有答案中没有涵盖的一个有趣的工具是使用#0用Go编写,它有一个标语,上面写着让JSON变得可接受!,这正是它所做的。

所以本质上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也具有零运行时依赖项。您可以下载二进制文件用于Linux、Mac、Windows或FreeBSD并运行它。

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

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

为什么我不应该只使用jq?

jq非常棒,比gron强大得多,但随之而来的是复杂性。gron的目标是让你更容易使用你已经知道的工具,比如grep和ses。

yum install php-cli之后使用PHP:

php -r " foreach(json_decode(file_get_contents('http://a.com/a.json'), true) as \$key => \$value) echo \$key.'='.\$value.\"\n\" ; "

如果安装了Node.js,这对我有用:

node -pe "require('${HOME}/.config/dev-utils.json').doToken"

使用ruby解析(默认情况下,解释器在/usr/bin/ruby中的所有macOS版本上都可用):

echo "${JSON}" | /usr/bin/ruby -e 'require "json"; puts JSON.parse(http://STDIN.read)["key1"]["nested_key_2"];'

您可以使用trdsql[1]来解析和转换JSON/CSV输入。

trdsql "select attr1,attr2 from sample.json"

你猜怎么着,你也可以使用where子句,就像在SQL中一样。输出在CSV、JSON等中。非常方便的工具。

根据我的经验,trdsql在处理属性嵌套值时有点问题,所以我在适当的时候使用qp[2]找到了解决方案。

cat sample.json | qp 'select attr1, attr2.detail.name where attr3=10'

注意,没有来自。

要查看结果,您可以使用超快速命令行json查看器工具jless来查看输出[3]。

  1. https://github.com/noborus/trdsql
  2. https://jless.io
  3. https://github.com/f5io/qp

如果您正在寻找原生Mac解决方案来解析JSON(无外部库等),那么这是适合您的。

此信息基于此处的一篇文章:https://www.macblog.org/parse-json-command-line-mac/

简而言之,因为早在Mac OS Yosemite就有一个名为osascript的运行苹果脚本的工具,但是如果你通过-l'Javascript'标志,你就可以运行javascript!使用JXA(JavaScript for Automation)就是这样叫的。

下面是为自己的项目读取JSON文件的示例。

DCMTK_JSON=$(curl -s https://formulae.brew.sh/api/bottle/dcmtk.json) # -s for silent moderead -r -d '' JXA <<EOFfunction run() {var json = JSON.parse(\`$DCMTK_JSON\`);return json.bottles.$2.url;}EOFDOWNLOAD_URL=$( osascript -l 'JavaScript' <<< "${JXA}" )echo "DOWNLOAD_URL=${DOWNLOAD_URL}"

这里发生的是我们将函数的输出存储到变量JXA中。然后我们可以简单地运行javascript以使用JSON.parse()解析JSON内容。然后只需将包含脚本的JXA变量传递给osascript工具,以便它可以运行javascript。在我的示例中,如果您测试这个,2美元指的是arm64_monterey。javascript立即运行的原因是因为特殊的函数run(),JXA会查找它并在完成后返回它的输出。

请注意,EOF(文件结尾)用于处理多行文本输入,结尾EOF不能在其前面有任何空格。

您可以通过简单地打开终端并键入下面的命令来测试这是否适合您

osascript -l 'JavaScript' -e 'var app = Application.currentApplication(); app.includeStandardAdditions = true; app.displayDialog("Hello from JavaScript!");

这应该会弹出一个弹出窗口,从javascript说你好

YAML处理器yq

考虑使用yq进行JSON处理。yq是一个轻量级和可移植的命令行YAML处理器(JSON是YAML的子集)。语法类似于jq

输入

{"name": "Angel","address": {"street": "Stairway to","city": "Heaven"}}

用法示例1

yq e '.name' $FILE

Angel

用法例2

yq有一个很好的内置功能来使JSON和YAML可用于grep

yq --output-format props $FILE

name = Angeladdress.street = Stairway toaddress.city = Heaven