全网就这一份:纯shell解析json
在linux中,解析json一般都会使用jq,jq固然好用,但是要额外安装就不太友好了。如果你在网上搜索通过shell来解析json的方法,你会发现都是些啥玩意儿。既然如此,那就动手撸一个吧。
有以下特性:
她能兼容jq的路径表达式
.name
.[0].name
.[0][0].name
- ...
- 纯shell,其实用的是awk。纯bash不好搞,就曲线救一下国呗
代码如下,拿去不谢。转载请标出处。转载请标出处。转载请标出处。
jsonq() {
json=$(cat)
awk -v json="$json" -v json_orgi="$json" -v key="$1" '
function strlastchar(s) {
return substr(s, length(s), 1)
}
function startwith(s, c) {
start = substr(s, 1, 1)
return start == c
}
function endwith(s, c) {
return strlastchar(s) == c
}
function innerstr(s) { # 取出括号/引号内的内容
return substr(s, 2, length(s)-2)
}
function strindex(s, n) { # 字符串通过下标取值,索引是从1开始的
return substr(s, n, 1)
}
function trim(s) {
sub("^[ \n]*", "", s);
sub("[ \n]*$", "", s);
return s
}
function findValueByKey(s, k) {
if ("\""k"\"" != substr(s, 1, length(k)+2)) {exit 0}
s = trim(s)
start = 0; stop = 0; layer = 0
for (i = 2 + length(k) + 1; i <= length(s); ++i) {
lastChar = substr(s, i - 1, 1)
currChar = substr(s, i, 1)
if (start <= 0) {
if (lastChar == ":") {
start = currChar == " " ? i + 1: i
if (currChar == "{" || currChar == "[") {
layer = 1
}
}
} else {
if (currChar == "{" || currChar == "[") {
++layer
}
if (currChar == "}" || currChar == "]") {
--layer
}
if ((currChar == "," || currChar == "}" || currChar == "]") && layer <= 0) {
stop = currChar == "," ? i : i + 1 + layer
break
}
}
}
if (start <= 0 || stop <= 0 || start > length(s) || stop > length(s) || start >= stop) {
exit 0
} else {
return trim(substr(s, start, stop - start))
}
}
function unquote(s) {
if (startwith(s, "\"")) {
s = substr(s, 2, length(s)-1)
}
if (endwith(s, "\"")) {
s = substr(s, 1, length(s)-1)
}
return s
}
BEGIN{
if (match(key, /^\./) == 0) {exit 0;}
sub(/\][ ]*\[/,"].[", key)
split(key, ks, ".")
if (length(ks) == 1) {print json; exit 0}
for (j = 2; j <= length(ks); j++) {
k = ks[j]
if (startwith(k, "[") && endwith(k, "]") == 1) { # [n]
idx = innerstr(k)
currentIdx = -1
# 找匹配对
pairs = ""
json = trim(json)
if (startwith(json, "[") == 0) {
exit 0
}
start = 2
cursor = 2
for (; cursor <= length(json); cursor++) {
current = strindex(json, cursor)
if (current == " " || current == "\n") {continue} # 忽略空白
if (current == "[" || current == "{") {
if (length(pairs) == 0) {start = cursor}
pairs = pairs""current
}
if (current == "]" || current == "}") {
if ((strlastchar(pairs) == "[" && current == "]") || (strlastchar(pairs) == "{" && current == "}")) {
pairs = substr(pairs, 1, length(pairs)-1) # 删掉最后一个字符
if (pairs == "") { # 匹配到了所有的左括号
currentIdx++
if (currentIdx == idx) {
json = substr(json, start, cursor-start+1)
break
}
}
} else {
pairs = pairs""current
}
}
}
} else {
# 到这里,就只能是{"key": "value"}或{"key":{}}或{"key":[{}]}
pairs = ""
json = trim(json)
if (startwith(json, "[")) {exit 0}
#if (!startwith(json, "\"") || !startwith(json, "{")) {json="\""json}
# 找匹配的键
start = 2
cursor = 2
noMatch = 0
for (; cursor <= length(json); cursor++) {
current = strindex(json, cursor)
if (current == " " || current == "\n" || current == ",") {continue} # 忽略空白和逗号
if (substr(json, cursor, length(k)+2) == "\""k"\"") {
json = findValueByKey(substr(json, cursor, length(json)-cursor+1), k)
break
} else {
noMatch = 1
}
if (noMatch) {
pos = match(substr(json, cursor+1, length(json)-cursor), /[^(\\")]"/)
ck = substr(substr(json, cursor+1, length(json)-cursor), 1, pos)
t = findValueByKey(substr(json, cursor, length(json)-cursor+1), ck)
tLen = length(t)
sub(/\\/, "\\\\", t)
pos = match(substr(json, cursor+1, length(json)-cursor), t)
if (pos != 0) {
cursor = cursor + pos + tLen
}
noMatch = 0
continue
}
}
}
}
if (json_orgi == json) { print;exit 0 }
print unquote(json)
}'
}
doublebackslash() {
echo "$1" | sed 's/\\/\\\\/g'
}
json='
{
"code": 200,
"msg": "success",
"data": {
"orderNo": "test_order_no"
}
}
'
json2='
[
[{
"name": "haxi",
"age": 18
},
{
"name": "hh",
"age": 28
}
],
[{
"name": "crz"
}]
]
'
json3='{"name":"ha\"ha", "age":18}'
json4='
{
"message": "{\"code\": 200}"
}
'
json5='{"id":"15331352","message":"ok"}'
json6='{"result":"/\n","status":"Done"}'
echo "$json" | jsonq ".data"
echo "$json" | jsonq ".data.orderNo"
echo "$json" | jsonq ".code"
echo "$json2" | jsonq ".[0][1].name"
echo "$json2" | jsonq ".[1][0].name"
echo "$json2" | jsonq ".[1][0]"
echo "$json2" | jsonq ".[0][0]"
echo "$json3" | jsonq ".age"
echo "$json3" | jsonq ".name"
echo "$json4" | jsonq ".message"
echo "$json5" | jsonq ".message"
echo "$json6" | jsonq ".result"
echo "$json6" | jsonq ".status"
echo "$json6" | jsonq ".data"
大佬牛逼,佩服佩服
请问如何遍历呀?有 demo 示例嘛?
请问如何遍历呀?有 demo 示例嘛?
不能遍历,请用jq