使用Unix工具解析JSON
我试图解析从curl请求返回的JSON,如下所示:使用Unix工具解析JSON,json,bash,parsing,node,Json,Bash,Parsing,Node,我试图解析从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]}' 如何打印特定字段(由-v k=text表示)?有许多工具专门设计用于从命令行操作JSON,与使用Awk相比,这些工具更容易、更可靠,例如:
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]}'
如何打印特定字段(由
-v k=text
表示)?有许多工具专门设计用于从命令行操作JSON,与使用Awk相比,这些工具更容易、更可靠,例如:
您还可以使用可能已经安装在系统上的工具(如使用的Python)来实现这一点,从而避免任何额外的依赖项,同时仍然可以使用适当的JSON解析器。以下假设您希望使用UTF-8,原始JSON应该使用UTF-8编码,并且大多数现代终端也使用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']"
常见问题
为什么不是纯shell解决方案?
该标准是一种非常有限的语言,不包含表示序列(列表或数组)或关联数组(在某些其他语言中也称为哈希表、映射、dict或对象)的工具。这使得在可移植的shell脚本中表示解析JSON的结果有些棘手。有,但如果键或值包含某些特殊字符,其中许多可能会中断
Bash 4及更高版本、zsh和ksh都支持阵列和关联阵列,但这些shell并不普遍可用(macOS在Bash 3停止更新Bash,原因是从GPLv2更改为GPLv3,而许多Linux系统没有现成安装zsh)。您可以编写一个可以在bash4或zsh中工作的脚本,目前大多数macOS、Linux和BSD系统上都可以使用其中的一个,但是要编写一个适用于这种多语言脚本的shebang行是很困难的
最后,在shell中编写一个完整的JSON解析器将是一个非常重要的依赖项,您可以使用现有的依赖项,如jq或Python。要实现一个好的实现,它不会是一行代码,甚至不会是一个五行代码的小片段
为什么不使用awk、sed或grep?
可以使用这些工具以已知的形状和格式(例如每行一个键)快速提取JSON。在其他答案中,有几个建议的例子
然而,这些工具是为基于行或基于记录的格式设计的;它们不是为递归解析可能包含转义字符的匹配分隔符而设计的
因此,这些使用awk/sed/grep的快速而肮脏的解决方案很可能是脆弱的,如果输入格式的某些方面发生更改,例如折叠空白,或向JSON对象添加额外级别的嵌套,或字符串中的转义引号,这些解决方案就会崩溃。一个足够健壮的解决方案能够在不中断的情况下处理所有JSON输入,它也将相当大和复杂,因此与添加另一个对jq
或Python的依赖关系没有太大区别
以前,由于shell脚本中的输入解析不好,我不得不处理大量被删除的客户数据,因此我从不建议使用这种方式可能很脆弱的快速而肮脏的方法。如果您正在做一些一次性处理,请参阅其他答案以获取建议,但我仍然强烈建议只使用现有的经过测试的JSON解析器
历史笔记
这个答案最初是推荐的,应该仍然有效,但使用起来比jq
要麻烦一些,并且取决于安装的独立JavaScript解释器,而独立JavaScript解释器不如Python解释器常见,因此上述答案可能更可取:
curl -s 'https://api.github.com/users/lambda' | jsawk -a 'return this.name'
这个答案最初也使用了问题中的Twitter API,但该API不再工作,因此很难复制示例进行测试,而新的Twitter API需要API密钥,因此我转而使用GitHub API,该API无需API密钥即可轻松使用。原问题的第一个答案是:
curl 'http://twitter.com/users/username.json' | jq -r '.text'
json2tsv id text < tweets.json | tsvawk '{print "tweet " $id " is: " $text}'
xidel -e 'json("http://twitter.com/users/username.json")("name")'
Invoke-WebRequest 'https://api.github.com/users/lambda' | select -ExpandProperty Content | ConvertFrom-Json
使用而不是使用awk
大概是这样的:
powershell 'curl -s "https://api.github.com/users/lambda" | ConvertFrom-Json'
{
"Name": "Tester",
"SomeList": [
"one",
"two",
"three"
]
curl-shttp://twitter.com/users/username.json | \
python-c“导入json,sys;obj=json.load(sys.stdin);打印(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的//\(^“\\\\”$\)//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
}
}
}'
curl-sL'http://twitter.com/users/username.json“|awk-F”,“-v k=“text””{
gsub(/{}/,“”)
对于使用Ruby和
$
或者更简洁地说:
$ < file.json ruby -r rubygems -r json -e "puts JSON.pretty_generate(JSON[STDIN.read]);"
$
由Martinar和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
array (
'BD' => 'BGD',
'BE' => 'BEL',
'BF' => 'BFA',
'BG' => 'BGR',
'BA' => 'BIH',
'BB' => 'BRB',
'WF' => 'WLF',
'BL' => 'BLM',
...
这个正则表达式比你想象的更健壮;例如,它可以很好地处理嵌入逗号和转义引号的字符串。我认为,再多做一点工作,你就可以制作出一个真正保证提取值的正则表达式,如果它是原子的(如果它有嵌套,那么正则表达式当然不能这样做)
为了进一步清理(尽管保留字符串的原始转义),您可以使用如下内容:|perl-pe's/“text”://;s/^/;s/“,$/”
(我这样做是为了。)
对于所有坚持您应该使用真正的JSON解析器的人来说——是的,这对于corr来说是必不可少的
#!/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
curl 'http://twitter.com/users/username.json' | jshon -e text
curl $url | grep $var | awk '{print $2}' | sed s/\"//g
curl $url | sed '/text/!d' | sed s/\"text\"://g | sed s/\"//g | sed s/\ //g
$ export JSON='{"hostname":"test","domainname":"example.com"}'
$ echo $JSON|php -r 'echo json_decode(file_get_contents("php://stdin"))->hostname;'
$ echo $JSON|php -r 'echo json_decode(fgets(STDIN))->hostname;'
$ curl 'https://twitter.com/users/username.json' | ./jq -r '.name'
xidel -e 'json("http://twitter.com/users/username.json")("name")'
xidel -e 'json("http://twitter.com/users/username.json").name'
$ node -pe 'JSON.parse(process.argv[1]).foo' "$(cat foobar.json)"
bar
access_token=$(curl <some horrible crap> | jsonlookup access token id)
#!/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
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))";
}
$ 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"
}
{ "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
}
}
}
$ 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
curl 'http://twitter.com/users/username.json' | py 'json.load(sys.stdin)["name"]'
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
echo "{\"field\": \"value\"}" | python -c 'import sys, json
print(json.load(sys.stdin)["field"])'
php -r 'var_export(json_decode(`curl http://twitter.com/users/username.json`, 1));'
curl http://country.io/iso3.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',
...
# 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
curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json
curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json | select -ExpandProperty id
powershell 'curl -s "https://api.github.com/users/lambda" | ConvertFrom-Json'
Invoke-WebRequest 'https://api.github.com/users/lambda' | select -ExpandProperty Content | ConvertFrom-Json
(New-Object PsObject -Property @{ Name = "Tester"; SomeList = @('one','two','three')}) | ConvertTo-Json
{
"Name": "Tester",
"SomeList": [
"one",
"two",
"three"
]
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
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_extract text "$(curl 'http://twitter.com/users/username.json')"
My status
$ json_extract friends_count "$(curl 'http://twitter.com/users/username.json')"
245
#!/usr/bin/env bash
my_val="$(json=$(<package.json) node -pe "JSON.parse(process.env.json)['version']")"
$ echo '{"key": "value"}' | fx "x => x.key"
value
$ echo '[1,2,3]' | fx "this.map(x => x * 2)"
[2, 4, 6]
$ echo '{"items": {"one": 1}}' | fx .items.one
1
$ echo '{"items": ["one", "two"]}' | fx "this.items" "this[1]"
two
$ echo '{"count": 0}' | fx "{...this, count: 1}"
{"count": 1}
awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "login") {print $4} }' test.json
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"