Bash Shell 处理 JSON 数据

2017-11-21 12491点热度 1条评论

最近小山在编写 Bash 脚本的时候遇到一个处理 JSON 的问题,需要更改指定键的值。

Bash 的功能实现都是基于系统内的命令或二进制,但是 Linux 下并没有这样的工具,只有一个名为 jq 的工具,但它只能解析 JSON,并不能更改和生成。

所以。。。只能用现有的文本处理命令和 JQ 写一个出来,由于 JSON 的复杂性,Bash Shell 想完美处理几乎是不可能的,所以我只写了更改,没有添加和删除。

Bash Shell 处理 JSON 数据

单个对象

如果要处理的 JSON 是单个对象,只有一个花括号 {}。

这样处理起来是很简单的,因为不存在重复的键值。

逻辑:

先用grep根据键来匹配并获取数据行,为了精准匹配,最好加上 -w 参数。

因为值是分为 string、init、bool 三种类型,字符串需要加上引号,其他则不需要,所以需要判断值是什么类型,可以用grep判断。

如果值的类型为字符串,就在更改的时候加上引号。

除了需要判断值的类型,还需要判断是不是最后一个键,因为最后一个键结尾是没有逗号的。

最后用 sed 把数据行替换掉。

替换前的 JSON:

{
    "site_mivm": "米V米",
    "url_mivm": "https://www.mivm.cn",
    "cdn_mivm": true,
    "type_mivm": 1
}

完整示例:

#!/bin/bash

json="test.json" # JSON 文件路径
json_data="`jq . $json`" # JSON 数据

set_json() {
[ $# -lt 2 ] && return 1 # 参数数量小于 2 返回 1
jq -e .$1 $json >/dev/null 2>&1 || return 2 # 键值不存在返回 2
local json_key=$1
local json_value=""
local line_data=$(echo "$json_data" | grep -w "\"$json_key\".*:" ) # 获取数据行
echo "$line_data" | grep -w "\".*\".*\".*\"" >/dev/null 2>&1
[ $? -eq 0 ] && json_value="\"$2\"" || json_value=$2 # 判断值是否为字符串
[ "${line_data:$((${#line_data}-1))}" = "," ] && json_value="${json_value}," # 判断数据行结尾是否有逗号
local json_new_data=$(echo "$json_data" | sed "s/$line_data/ \"$json_key\": $json_value/") # 替换数据行
echo "$json_new_data" | jq -e . >/dev/null 2>&1 || return 3 # 校验替换后的数据 失败返回 3
json_data="$json_new_data" && echo "$json_data" > $json
}

set_json "site_mivm" "米V米 - 希望可以找到你想到的"

替换后的 JSON:

{
    "site_mivm": "米V米 - 希望可以找到你想到的",
    "url_mivm": "https://www.mivm.cn",
    "cdn_mivm": true,
    "type_mivm": 1
}

此方法可以非常方便的更改单个对象的 JSON 数据,当然,添加和删除也可以,只需要稍微改动一下就可以了。

示例文件下载 (不要直接复制上面的):https://cdn.mivm.cn/www.mivm.cn/archives/bash-shell-json/shell-json.zip

PS: 总比某些脚本更改一个键值写一个函数强

多个对象或数组

如果你遇到的 JSON 有多个对象或数组,比如这样的:

{
    "site":[
        {
            "name": "米V米",
            "url": "https://www.mivm.cn"
        },
        {
            "name": "米V米",
            "url": "https://www.mivm.cn"
        },
        {
            "name": "米V米",
            "url": "https://www.mivm.cn"
        }
    ],
    "cdn": true,
    "type": {
        "1":"blog",
        "2":"blog",
        "3":"blog"
    }
}

那么,更改起来可能就有点麻烦了,因为就不能使用上面那种方法了,多个对象之间,键值是允许重复的,需要另辟奇径。

很可惜的是,小山想了一天也没想出来通用的方法,可以不受数量限制更改。

只能是有针对性的更改,如果你有完美处理的方法,或者想跟小山进行探讨,可以加入 QQ 群小山


以上方法为小山原创,如有雷同,纯属巧合。

如果你有更好的方法,欢迎加入 QQ 群小山探讨。

微信公众号二维码

微信扫描二维码关注我们

如果觉得文章有帮助到你,可以点击下方的打赏按钮赞助下服务器费用。

小山

一个什么都不会但要装作很厉害的人

文章评论

  • 路人甲

    jq 可以修改的 ,setpath 函数很神奇,你可以去试试。

    2018-04-19
  • 此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据