You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需jq,如何在Shell脚本中更新JSON文件?

不依赖jq在Shell脚本中操作JSON的简便方案

之前用jq读写JSON很方便,但不是所有Bash环境都预装jq。我尝试用sed、awk写了复杂的字符串处理脚本(如下),但语法繁琐还没法处理嵌套JSON结构,想找更简便的替代方法:

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"
}

推荐方案:用Python内置JSON模块(通用且靠谱)

几乎所有类Unix系统都预装了Python,其标准库的json模块能完美处理JSON的语法规则(包括嵌套、转义字符、空格格式等),比sed/awk的正则方法可靠太多。

替换你现有函数的简化版本:

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

    # 确保文件存在,不存在则创建空JSON
    [[ ! -f "$filename" ]] && echo "{}" > "$filename"

    # 用Python脚本处理JSON更新
    python3 -c "
import json, os
with open('$filename', 'r+') as f:
    try:
        data = json.load(f)
    except json.JSONDecodeError:
        data = {}
    data['$key'] = '$value'
    f.seek(0)
    json.dump(data, f, indent=2)
    f.truncate()
"
}

这个函数支持:

  • 自动创建空JSON文件
  • 处理已有键的更新、新键的添加
  • 自动格式化JSON(带缩进)
  • 原生支持嵌套JSON结构(如果需要更新嵌套键,只需修改Python代码里的data['$key']为data['parent']['child']这类层级访问)

备选方案:用Perl的JSON::PP模块(核心模块无需额外安装)

如果系统预装Perl,JSON::PP是Perl的核心模块,不需要额外安装,用法类似Python:

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

    [[ ! -f "$filename" ]] && echo "{}" > "$filename"

    perl -MJSON::PP -0777 -ne '
        my $data = eval { decode_json($_) } || {};
        $data->{"'"$key"'"} = "'"$value"'";
        print encode_json($data) . "\n";
    ' "$filename" > "$filename.tmp" && mv "$filename.tmp" "$filename"
}

为什么不推荐继续用sed/awk?

sed和awk是基于正则的文本处理工具,而JSON是有层级结构的语法,正则无法可靠处理递归嵌套、字符串中的特殊字符(比如包含引号、反斜杠的字符串),很容易破坏JSON的合法性。你的现有脚本只能处理扁平的、格式规范的简单JSON,一旦遇到嵌套或者不标准的空格格式,就会出错。

如果必须用纯shell工具(比如极端环境下连Python/Perl都没有),那只能限制在非常简单的扁平JSON场景,并且要处理大量边界情况,但这几乎不值得投入精力——优先考虑用系统自带的脚本语言处理更靠谱。

内容的提问来源于stack exchange,提问作者Alfred Chow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 19:28:16