提问人:auser 提问时间:12/24/2009 最后编辑:wejoeyauser 更新时间:4/17/2023 访问量:1679373
使用 Unix 工具解析 JSON
Parsing JSON with Unix tools
问:
我正在尝试解析从 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
答:
有许多工具是专门为从命令行操作 JSON 而设计的,并且比使用 Awk 操作要容易得多,也更可靠,例如 jq
:
curl -s 'https://api.github.com/users/lambda' | jq -r '.name'
您还可以使用可能已经安装在系统上的工具(例如使用 json
模块的 Python)来执行此操作,从而避免任何额外的依赖关系,同时仍然具有适当的 JSON 解析器的好处。下面假设您要使用 UTF-8,原始 JSON 应该用 UTF-8 进行编码,并且大多数现代终端也使用这种编码:
蟒蛇 3:
curl -s 'https://api.github.com/users/lambda' | \
python3 -c "import sys, json; print(json.load(sys.stdin)['name'])"
蟒蛇 2:
export PYTHONIOENCODING=utf8
curl -s 'https://api.github.com/users/lambda' | \
python2 -c "import sys, json; print json.load(sys.stdin)['name']"
常见问题解答
为什么不是纯壳解决方案?
标准的 POSIX/Single Unix 规范 shell 是一种非常有限的语言,它不包含用于表示序列(列表或数组)或关联数组(在其他语言中也称为哈希表、映射、字典或对象)的工具。这使得在可移植的 shell 脚本中表示解析 JSON 的结果有些棘手。有一些黑客方法可以做到这一点,但如果键或值包含某些特殊字符,其中许多方法可能会中断。
Bash 4 及更高版本、zsh 和 ksh 支持数组和关联数组,但这些 shell 并非普遍可用(macOS 在 Bash 3 中停止更新 Bash,因为从 GPLv2 更改为 GPLv3,而许多 Linux 系统没有开箱即用地安装 zsh)。你可以编写一个可以在 Bash 4 或 zsh 中工作的脚本,其中之一在当今大多数 macOS、Linux 和 BSD 系统上都可用,但要编写一个适用于这种多语言脚本的 shebang 行是很困难的。
最后,在 shell 中编写一个完整的 JSON 解析器将是一个足够重要的依赖项,您不妨使用现有的依赖项,例如 jq 或 Python。它不会是一行,甚至不是一小段五行的片段,就可以很好地实现。
为什么不使用 awk、sed 或 grep?
可以使用这些工具从具有已知形状并以已知方式格式化的 JSON 中进行一些快速提取,例如每行一个键。在其他答案中有几个建议的例子。
但是,这些工具是为基于行或基于记录的格式而设计的;它们不是为递归分析具有可能的转义字符的匹配分隔符而设计的。
因此,这些使用 awk/sed/grep 的快速而肮脏的解决方案可能很脆弱,如果输入格式的某些方面发生变化,例如折叠空格,或向 JSON 对象添加额外的嵌套级别,或字符串中的转义引号,它们就会中断。一个足够健壮的解决方案,可以在不中断的情况下处理所有JSON输入,也将是相当庞大和复杂的,因此与添加另一个对Python的依赖关系没有太大区别。jq
我以前不得不处理由于 shell 脚本中的输入解析不佳而被删除的大量客户数据,因此我从不推荐以这种方式可能很脆弱的快速而肮脏的方法。如果您正在进行一些一次性处理,请参阅其他答案以获取建议,但我仍然强烈建议您只使用现有的经过测试的 JSON 解析器。
历史笔记
这个答案最初推荐的是 jsawk,它应该仍然可以工作,但使用起来比 更麻烦一些,并且依赖于安装一个独立的 JavaScript 解释器,它比 Python 解释器更不常见,所以上面的答案可能更可取:jq
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'
评论
print
PYTHONIOENCODING=<desired codec>
print()
curl -s
等价于 ,而 means 即没有字符串引号。curl --silent
jq -r
jq --raw-output
使用 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) 删除了 /usr/bin/python
,因此我们必须用于 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']);"
评论
json.load(sys.stdin)['"key']"
curl -sL httpbin.org/ip | python -c "import json,sys; print json.load(sys.stdin)['origin']"
/usr/bin/python
在macOS上不存在,所以现在需要使用python3。12.3
你问过如何搬起石头砸自己的脚,我在这里提供弹药:
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]);"
评论
;
跟随 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
评论
| grep field
jq
通常不会在 Python 安装时安装。另外,一旦你进入 Python,你不妨一路走来,用import json...
为了快速提取特定键的值,我个人喜欢使用“grep -o”,它只返回正则表达式的匹配项。例如,要从推文中获取“文本”字段,如下所示:
grep -Po '"text":.*?[^\\]",' tweets.json
这个正则表达式比你想象的要强大;例如,它可以很好地处理嵌入逗号和转义引号的字符串。我认为,只要再做一点工作,你就可以做一个实际上可以保证提取值的,如果它是原子的。(如果它有嵌套,那么正则表达式当然不能这样做。
为了进一步清理(尽管保持字符串的原始转义),您可以使用类似的东西: .(我这样做是为了这个分析。| perl -pe 's/"text"://; s/^"//; s/",$//'
对于所有坚持使用真正的JSON解析器的仇恨者 - 是的,这对于正确性至关重要,但是
- 要进行非常快速的分析,例如计算值以检查数据清理错误或大致了解数据,在命令行上敲出一些东西会更快。打开编辑器编写脚本会分散注意力。
grep -o
比 Python 标准库快几个数量级,至少在对推文(每个推文为 ~2 KB)执行此操作时是这样。我不确定这是否只是因为速度慢(我应该在某个时候与 yajl 进行比较);但原则上,正则表达式应该更快,因为它是有限状态并且更可优化,而不是必须支持递归的解析器,在这种情况下,花费大量 CPU 为你不关心的结构构建树。(如果有人编写了一个有限状态换能器来执行适当的(深度限制)JSON 解析,那就太棒了!同时,我们有“grep -o”。json
json
为了编写可维护的代码,我总是使用真正的解析库。我还没有尝试过 jsawk,但如果它运行良好,这将解决 #1 点。
最后一个更古怪的解决方案:我编写了一个脚本,它使用 Python 并将您想要的键提取到制表符分隔的列中;然后,我通过管道传递一个包装器,该包装器允许对列进行命名访问。在这里:json2tsv 和 tsvawk 脚本。因此,对于此示例,它将是:json
awk
json2tsv id text < tweets.json | tsvawk '{print "tweet " $id " is: " $text}'
这种方法不能解决 #2,比单个 Python 脚本效率低下,而且有点脆弱:它强制对字符串值中的换行符和制表符进行规范化,以配合 awk 的字段/记录分隔的世界视图。但它确实让你停留在命令行上,比 .grep -o
评论
grep -Po '"text":(\d*?,|.*?[^\\]",)'
jq .name
在命令行上运行,不需要“打开编辑器来编写脚本”。2. 你的正则表达式产生错误结果的速度有多快并不重要
| grep -Po '"text":.*?[^\\]",'|awk -F':' '{print $2}'
-P
grep --version
grep (BSD grep) 2.5.1-FreeBSD
grep -Eo '"text":.*?[^\\]",' tweets.json
基于这里的一些建议(尤其是在评论中)建议使用 Python,我很失望没有找到一个例子。
因此,这里有一个单行代码,用于从一些 JSON 数据中获取单个值。它假定您正在(从某处)通过管道传输数据,因此在脚本上下文中应该很有用。
echo '{"hostname":"test","domainname":"example.com"}' | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["hostname"]'
评论
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
obj[0]
解析时导致错误。删除后工作正常。{ "port":5555 }
[0]
print(obj["hostname"])
print obj["hostname"]
你可以尝试这样的事情——
curl -s 'http://twitter.com/users/jaypalsingh.json' |
awk -F=":" -v RS="," '$1~/"text"/ {print}'
更新 (2020)
我对外部工具(例如 Python)的最大问题是您必须处理包管理器和依赖项才能安装它们。
但是,现在我们有一个独立的静态工具,可以通过 GitHub Releases 和 Webi (webinstall.dev/jq) 轻松跨平台安装,我建议:jq
Mac、Linux:
curl -sS https://webi.sh/jq | bash
视窗 10:
curl.exe -A MS https://webi.ms/jq | powershell
原创 (2011)
TickTick 是一个用 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()``; 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
评论
你可以使用 jshon
:
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将是“文本”来获取该变量的响应。
真的,我认为我正在做的 OP 唯一遗漏的就是 grep 与他寻求的特定变量的行。AWK 抓取行中的第二项,然后用 sed 去掉引号。
比我聪明的人可能会用 AWK 或 grep 完成整个思考。
现在,你可以用 just sed 完成这一切:
curl $url | sed '/text/!d' | sed s/\"text\"://g | sed s/\"//g | sed s/\ //g
因此,没有 AWK,没有 grep......我不知道为什么我以前没有想到这一点。嗯......
评论
grep | awk | sed
sed | sed | sed
curl "$url" | sed '/text/!d;s/\"text\"://g;s/\"//g;s/\ //g'
使用 PHP CLI 解析 JSON
这可以说是题外话,但由于优先权占主导地位,如果不提及我们值得信赖和忠实的 PHP,这个问题仍然是不完整的,我说得对吗?
它使用相同的示例 JSON,但让我们将其分配给一个变量以减少模糊性。
export JSON='{"hostname":"test","domainname":"example.com"}'
现在,为了PHP的优点,它使用了file_get_contents和 php://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)
$argn
你可以下载适用于你的平台的 jq
二进制文件并运行 ():chmod +x jq
$ curl 'https://twitter.com/users/username.json' | ./jq -r '.name'
它从 json 对象中提取属性。"name"
jq
主页说它就像 JSON 数据一样。sed
评论
curl -s https://api.example.com/jobs | jq '.jobs[] | {id, o: .owner.username, dateCreated, s: .status.state}'
也有XML文件的人,可能想看看我的Xidel。它是一个命令行界面,无依赖的 JSONiq 处理器。(也就是说,它还支持用于XML或JSON处理的XQuery。
问题中的例子是:
xidel -e 'json("http://twitter.com/users/username.json")("name")'
或者使用我自己的非标准扩展语法:
xidel -e 'json("http://twitter.com/users/username.json").name'
评论
xidel -s https://api.github.com/users/lambda -e 'name'
-e '$json/name'
-e '($json).name'
使用 Node.js
如果系统安装了 Node.js,则可以将打印和评估脚本标志与 JSON.parse
一起使用,以提取所需的任何值。-p
-e
一个简单的例子,使用JSON字符串并拉出“foo”的值:{ "foo": "bar" }
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
评论
node -p -e 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
curl -s https://api.github.com/users/trevorsenior | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin').toString()).name"
cat package.json | node -pe 'JSON.parse(fs.readFileSync(0)).version'
在 shell 脚本中解析 JSON 是很痛苦的。使用更合适的语言创建一个工具,以符合 shell 脚本约定的方式提取 JSON 属性。您可以使用新工具解决即时的 shell 脚本问题,然后将其添加到工具包中以备将来使用。
例如,考虑一个工具 jsonlookup,如果我说它将返回在标准输入的属性访问中定义的属性令牌中定义的属性 id,这可能是 JSON 数据。如果该属性不存在,则该工具不返回任何内容(退出状态 1)。如果解析失败,则退出状态 2 并显示一条消息到标准错误。如果查找成功,该工具将打印属性的值。jsonlookup access token id
创建了一个用于提取 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
将 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 \) ]; 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"
}
评论
curl http://foo | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["environment"][0]["name"]'
sys.stdout.write()
如果您希望它同时适用于 Python 2 和 3。
getJsonVal() { py -x "json.dumps(json.loads(x)$1, sort_keys=True, indent=4)"; }
对于更复杂的 JSON 解析,我建议使用 Python jsonpath 模块(由 Stefan Goessner 编写)-
安装它 -
sudo easy_install -U jsonpath
使用它 -
示例 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 包装代码。
评论
jsonpath
jsonpath_rw
/usr/bin/python -m pip install jsonpath-rw
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;"
这是pythonpy的一个很好的用例:
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":"[email protected]"}' username
parse_json '{"username":"john doe","email":"[email protected]"}' email
--- outputs ---
john, doe
[email protected]
评论
sed
awk
bash
使用 Python 的两行代码。如果您正在编写单个 .sh 文件并且不想依赖另一个 .py 文件,则效果特别好。它还利用了管道的使用。 可以替换为将 JSON 文件打印到标准输出的任何内容。|
echo "{\"field\": \"value\"}"
echo "{\"field\": \"value\"}" | python -c 'import sys, json
print(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]} } }'
评论
sed
的东西都不应该再获得任何赞成票。
如果您安装了 PHP 解释器:
php -r 'var_export(json_decode(`curl http://twitter.com/users/username.json`, 1));'
例如:
我们有一个资源,它提供了带有国家/地区 ISO 代码的 JSON 内容: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',
...
如果你有嵌套数组,这个输出看起来会好得多......
我用它来从 ffprobe 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.key
value
$ curl -s http://0/file.json | json-query my.keys.
key_1
key_2
key_3
$ curl -s http://0/file.json | json-query my.keys.2
value_2
评论
不幸的是,使用的最高票数答案返回了在我的方案中不起作用的完整匹配,但如果你知道 JSON 格式将保持不变,则可以使用 lookbehind 和 lookahead 来提取所需的值。grep
# 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
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 方法可以在没有 curl 的情况下做到这一点,那就是:
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 页面: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 实现并利用了 Python。您会注意到,一旦我们将 Python 对象导入到 Python 字典中,我们就可以使用 Python 语法来导航字典。要浏览上述内容,语法为:jq
unquote
json
data
data[ "authInfo" ]
data[ "authInfo" ][ "tokenServicesUrl" ]
通过在 Bash 中使用 magic,我们省略并只在数据右侧提供 Python 文本,即data
jq
jq '[ "authInfo" ]'
jq '[ "authInfo" ][ "tokenServicesUrl" ]'
请注意,在没有参数的情况下,jq 充当 JSON 美化器。使用参数,我们可以使用 Python 语法从字典中提取我们想要的任何内容,包括导航字典和数组元素。
下面是 Bash Python 混合函数:
#!/bin/bash -xe
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 )"
}
下面是 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=$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
评论
有一种更简单的方法可以从 JSON 字符串中获取属性。以文件为例,试试这个:package.json
#!/usr/bin/env bash
my_val="$(json=$(<package.json) node -pe "JSON.parse(process.env.json)['version']")"
我们使用 ,因为这会将文件的内容作为字符串获取到 Node.js 中,而不会有恶意内容逃脱其引用并被解析为代码的风险。process.env
评论
require()
JSON.parse()
JSON.parse()
JSON.parse()
的 shell 变量替换到代码中。您假设放置文本反引号将使内容保持文本,但这是一个完全不安全的假设,因为文本反引号可以存在于文件内容(以及变量)中,因此可以终止引用并进入未引用的上下文,其中值作为代码执行。
还有一个非常简单但功能强大的 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
您可以使用 spread 运算符更新现有 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 数据。
让我们考虑一下文件中的这个示例 JSON 数据test.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 数组、declare、grep -P、lookbehind、lookahead、Python、Perl、Ruby 甚至 Bash 都不可用。
其余的答案根本无法很好地工作。JavaScript 听起来很熟悉,但罐子上写着雀巢咖啡 - 所以这是不行的,太:)即使可用,对于我的简单需求 - 它们也会矫枉过正且速度缓慢。
然而,对我来说,从调制解调器的 JSON 格式回复中获取许多变量非常重要。我在 Bourne shell () 中使用它的路由器上有一个非常精简的 BusyBox!单独使用 AWK 没有任何问题:只需设置分隔符并读取数据即可。对于单个变量,仅此而已!sh
awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "login") {print $4} }' test.json
还记得我没有任何数组吗?我必须在 AWK 解析的数据中将数据分配给我在 shell 脚本中需要的 11 个变量。无论我在哪里看,这都被认为是一个不可能完成的任务。这也没有问题。
我的解决方案很简单。此代码将:
从问题中解析 .json 文件(实际上,我从最受好评的答案中借用了一个工作数据样本)并挑选出引用的数据,加上
从 awk 中创建 shell 变量,分配自由命名的 shell 变量名称。
eval $( curl -s 'https://api.github.com/users/lambda' | awk ' 开始 { FS=“”“;RS=“,” }; { if ($2 == “登录”) { print “Login=”“$4”“” } if ($2 == “name”) { print “Name=”“$4”“” } if ($2 == “updated_at”) { print “Updated=”“$4”“” } }' ) 回声“$Login,$Name,$Updated”
里面的空白没有任何问题。在我的使用中,相同的命令解析一个长的单行输出。使用 eval 时,此解决方案仅适用于可信数据。
很容易使它适应拾取未引用的数据。对于大量变量,可以使用 else if 实现边际速度增益。缺少数组显然意味着:没有额外的摆弄就没有多个记录。但是,在阵列可用的情况下,调整此解决方案是一项简单的任务。
@maikel 的 sed 答案几乎有效(但我不能对此发表评论)。对于我格式良好的数据 - 它有效。这里使用的示例不是那么多(缺少引号会把它扔掉)。它很复杂,很难修改。另外,我不喜欢必须进行 11 次调用来提取 11 个变量。为什么?我计时了 100 个循环,提取了 9 个变量:sed 函数用了 48.99 秒,我的解决方案用了 0.91 秒!不公平?仅对 9 个变量进行单次提取:0.51 秒对 0.02 秒。
我需要 Bash 中一些简短的东西,并且可以在 Python 2.7 和 3 的 vanilla Linux LSB 和 Mac OS 之外没有依赖项的情况下运行并处理错误,例如,将报告 JSON 解析错误和缺少属性错误而不会喷出 Python 异常:
json-extract () {
if [[ "$1" == "" || "$1" == "-h" || "$1" == "-?" || "$1" == "--help" ]] ; then
echo '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'
else
python -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(带)和 shell 的书的答案:JSON.sh,4.7 KB。 local
egrep
这个东西有很多测试用例,所以它应该是正确的。它也是可管道的。它用于 Bash 的包管理器 bpkg。
评论
以下是 Node.js 就绪环境的简单方法:
curl -L https://github.com/trentm/json/raw/master/lib/json.js > json
chmod +x json
echo '{"hello":{"hi":"there"}}' | ./json "hello.hi"
现有答案中没有涵盖的一个有趣的工具是使用用 Go 编写的 gron
,它有一个标语,上面写着 Make JSON greppable!,这正是它的作用。
因此,基本上将 JSON 分解为离散的赋值,查看它的绝对“路径”。与其他工具相比,它的主要优点是允许在不知道要搜索的记录的嵌套程度的情况下搜索值,而不会破坏原始的 JSON 结构gron
jq
例如,我想从以下链接搜索该字段,我只是这样做'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"
}
就这么简单。请注意(ungron 的缩写)如何从搜索路径中重建 JSON。只需要将搜索过滤到所需的路径,而不是将搜索表达式计算为正则表达式,而是将其计算为固定字符串(本质上是gron -u
fgrep
grep -F
)
另一个示例,用于搜索字符串以查看记录在嵌套结构中的位置
% echo '{"foo":{"bar":{"zoo":{"moo":"fine"}}}}' | gron | fgrep "fine"
json.foo.bar.zoo.moo = "fine";
它还支持使用命令行标志流式处理 JSON,您可以在其中连续对匹配记录的输入流进行排序。也具有零运行时依赖项。您可以下载适用于 Linux、Mac、Windows 或 FreeBSD 的二进制文件并运行它。-s
gron
更多使用示例和行程可以在官方 Github 页面 - 高级用法中找到
至于为什么你可以使用其他JSON解析工具,请参阅项目页面的作者注释。gron
为什么我不应该只使用 jq?
JQ 很棒,而且比 Gron 强大得多,但随之而来的是复杂性。GRON 旨在让您更轻松地使用您已经知道的工具,例如 grep 和 sed。
评论
在以下之后使用 PHP:yum install php-cli
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 解析(默认情况下,解释器在所有 macOS 版本上都可用):/usr/bin/ruby
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"
您也可以像在 SQL 中一样使用 where 子句。以CSV,JSON等格式输出。非常方便的工具。
根据我的经验,trdsql在处理属性嵌套值时有点问题,所以我在适当的时候使用qp [2]找到了解决方案。
cat sample.json | qp 'select attr1, attr2.detail.name where attr3=10'
请注意,没有 FROM。
要查看结果,您可以使用超快速的命令行 json 查看器工具,jless 查看输出 [3]。
Clickhouse 的街区里有一个新孩子。你可以从[4]中看到它的能力。
- https://github.com/noborus/trdsql
- https://jless.io
- https://github.com/f5io/qp
- https://clickhouse.com/blog/extracting-converting-querying-local-files-with-sql-clickhouse-local
如果您正在寻找一种原生 Mac 解决方案来解析 JSON(无外部库等),那么这是为您准备的。
此信息基于此处的一篇文章:https://www.macblog.org/parse-json-command-line-mac/
简而言之,由于早在 Mac OS Yosemite 就有用于运行 os ascript 的 apple 脚本的工具,但是如果您传递 -l 'Javascript' 标志,您就可以运行 javascript!这就是所谓的使用 JXA(用于自动化的 JavaScript)。
下面是为我自己的项目读取 JSON 文件的示例。
DCMTK_JSON=$(curl -s https://formulae.brew.sh/api/bottle/dcmtk.json) # -s for silent mode
read -r -d '' JXA <<EOF
function run() {
var json = JSON.parse(\`$DCMTK_JSON\`);
return json.bottles.$2.url;
}
EOF
DOWNLOAD_URL=$( osascript -l 'JavaScript' <<< "${JXA}" )
echo "DOWNLOAD_URL=${DOWNLOAD_URL}"
这里发生的事情是,我们将函数的输出存储到变量 JXA 中。然后,我们可以简单地运行 javascript 来使用 JSON.parse() 解析 JSON 内容。然后,只需将包含脚本的 JXA 变量传递给 osascript 工具,以便它可以运行 javascript。在我的示例中,如果您对此进行测试,则 2 美元指的是 arm64_monterey。javascript 立即运行的原因是 特殊函数 ,JXA 会寻找该函数并在完成后返回其输出。run()
请注意,EOF(文件末尾)用于处理多行文本输入,并且结尾 EOF 前面不能有任何空格来工作。
您只需打开终端并键入以下命令即可测试这是否适合您
osascript -l 'JavaScript' -e 'var app = Application.currentApplication(); app.includeStandardAdditions = true; app.displayDialog("Hello from JavaScript!");
这应该会弹出一个窗口,从 javascript 打招呼
YAML 处理器 yq
请考虑使用 yq 进行 JSON 处理。 是一个轻量级且可移植的命令行 YAML 处理器(JSON 是 YAML 的子集)。
语法类似于 。yq
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 = Angel
address.street = Stairway to
address.city = Heaven
pip3 install jq
parse() {
key=$1
python3 -c "
import sys
import jq
import json
input = json.load(sys.stdin)
output = jq.compile('$key').input(input).all()
if(isinstance(output, list)):
output = ' '.join(output)
print(output)
"
}
name=$(aws emr describe-cluster --cluster-id $id | parse ".Cluster.Name")
echo $name
对于具有任何 python 3 环境的 python 用户:
python -m json.tool
它将从 stdin 获取 json 输入并很好地打印出 json。
要进行测试,假设你有一个 json 文件:test.json。并将 grep 传递给 grep your_key:
cat test.json | python -m json.tool
cat test.json | python -m json.tool | grep 'your_key'
假设你有 curl 的输出:
curl -s 'https://api.github.com/users/lambda' | python -m json.tool
评论
grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' package.json