如何在不使用jq的情况下在shell脚本中更新JSON文件?

问题描述 投票:0回答:1

我正在使用 shell 脚本来更新 JSON。以前我使用 jq 命令来读取/写入 JSON 对象。然而似乎并不是每个 bash 环境都支持 jq 命令。我尝试通过使用 awk、sed 和 gsup 操作字符串来解决这个问题。

但是语法很复杂,如下所示:

update_json_key() {
    local key="$1"
    local value="$2"
    local filename="$3"

    if [[ ! -f "$filename" ]]; then
      echo "{}" > "$filename"
    fi

    SED_INPLACE=""

    if [[ "$OSTYPE" == "darwin"* ]]; then
        SED_INPLACE="-i ''"  # macOS requires an empty extension for in-place editing
    else
        SED_INPLACE="-i"     # Linux
    fi

    # Ensure the file exists and is a valid JSON object
    if [ ! -s "$filename" ] || ! grep -q "{" "$filename"; then
        echo "{}" > "$filename"
    fi

    # Check if the key exists
    if grep -q "\"$key\": " "$filename"; then
        # Key exists, update it
        # This regex ensures we don't capture more than we need by being specific with our patterns
        sed $SED_INPLACE "s/\"$key\": \"[^\"]*\"/\"$key\": \"$value\"/" "$filename"
    else
        # Key does not exist, add it
        if grep -q "^{}$" "$filename"; then
            # File has only empty JSON, directly add key without comma
            sed $SED_INPLACE "s/{}/{\"$key\": \"$value\"}/" "$filename"
        else
            # File is non-empty, append key before the last closing brace
            # This ensures that we correctly find the last closing brace even when it's not at the very end
            sed $SED_INPLACE "s/\(.*\)}$/\1, \"$key\": \"$value\"}/" "$filename"
        fi
    fi

    if [ -e "$filename''" ]; then
        rm -f "$filename''"
    fi

    if [[ -e "$filename" ]] && [[ $(wc -l < "$filename") -gt 1 ]]; then
        raw_json=$(sed -e ':a' -e 'N' -e '$!ba' -e 's/\n//g' -e 's/": \?"/": "/g' -e 's/, \?"/,"/g' "$filename")
        rm -f $filename
        echo "$raw_json" > "$filename"
    fi

    formatted_json=$(awk 'BEGIN {
        FS=",";
        print "{"
    }
    {
        gsub(/[{}]/, "");
        n = split($0, a, ",");
        for (i = 1; i <= n; i++) {
            gsub(/^[[:space:]]+|[[:space:]]+$/, "", a[i]);
            print "  " a[i] (i < n ? "," : "");
        }
    }
    END {
        print "}"
    }' "$filename")

    rm -f $filename
    echo "$formatted_json" > "$filename"
}

此语法尚未处理任何嵌套 JSON 对象迭代。我在想是否有一种更简单的方法可以在不使用 jq 的情况下使用 shell 脚本操作 JSON 对象。

json shell awk sed jq
1个回答
0
投票

似乎不是每个 bash 环境都支持 jq 命令

首先确定这种情况有多常见,然后有多少用户可以安装

jq
以及有多少用户不能安装
jq
。如果后一种情况很少见,请考虑使用
jq
并告知您的脚本需要
jq
才能工作。

尝试通过使用 awk、sed 操作字符串来解决此问题 和 gsup。

我不知道最后一个名为

awk
sed
的工具不支持使用 JSON。存在 GNU gawkextlib
AWK
 变体,它确实支持使用 JSON 和 XML,但您需要收集依赖项并构建它,这样做的要求会增加脚本用户的负担。

使用 shell 脚本操作 JSON 对象的更简单方法,无需使用 jq

您可能会考虑使用 json

 标准库中的 
python
,因为 
python
(及其标准库)通常安装在 Linux 计算机上,警告:您应该首先轮询用户的代表性样本以确定如果他们有
jq
python
或两者都可运行。

假设 JSON 文件中最顶层的结构始终是 Object 的简单示例。使用以下内容创建

upsert.py

import argparse
import json

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='UPDATE or INSERT key-value into JSON document')
    parser.add_argument('key')
    parser.add_argument('value')
    parser.add_argument('filename')
    args = parser.parse_args()
    with open(args.filename, 'r') as f:
        data = json.load(f)
    data[args.key] = args.value
    with open(args.filename, 'w') as f:
        json.dump(data, f)

并让

file.json
内容为

{"A":"Able","B":"Baker","C":"Charlie"}

然后

python upsert.py 'D' 'Dog' file.json

将 file.json 内容更改为

{"A": "Able", "B": "Baker", "C": "Charlie", "D": "Dog"}

观察到,除了

D
的新条目之外,在
:
,
之后添加了空格,这是默认行为。如果您想应用不同的格式,请参阅
json.dumps
文档

(在 Python 2.7.18 和 Python 3.10.12 中测试)

© www.soinside.com 2019 - 2024. All rights reserved.