使用 jq 和 find 仅编辑与前提条件匹配的文件

使用 jq 和 find 仅编辑与前提条件匹配的文件

我有相当多的 JSON 文件,它们具有不同的版本作为属性。我想修改仅与特定谓词匹配的文件。这是后续使用 find 和 jq 进行就地编辑

这对我来说很重要,因为如果我修改数百个文件并打开 PR,除了我想要更改的内容之外,我不需要任何其他内容。例如,修改缩进看起来整个文件都被修改了,审阅者很难看到实际的差异。

为了说明我的意思,请将以下两个文件放入名为 的文件夹中json

{
    "identifier": "1",
    "version" : "1.0"
}

请注意版本后面的尾随空格,这是故意的。

{
    "identifier": "2",
    "version": "2.0"
}

这些片段中的缩进为 4 个空格(vscode 中默认)。

运行指向 json 文件夹的以下脚本:

find json \
    -name '*.json' \
    -type f -exec sh -c '
    tmpfile=$(mktemp)
    for pathname do
        cp -- "$pathname" "$tmpfile" &&
        jq "select(.version == \"2.0\").identifier |= \"\"" <"$tmpfile" >"$pathname"
    done
    rm -f "$tmpfile"' sh {} +

我期望的结果是,只有具有版本的文件2.0才会被修改,除了identifier.最终结果是这样的:

第一个文件具有原始标识符(预期),但缩进现在为 2 个空格(非预期),并且后面的空格version消失了(非预期):

{
  "identifier": "1",
  "version": "1.0"
}

第二个文件具有空标识符(预期),并且缩进现在为 2 个空格(非预期):

{
  "identifier": "",
  "version": "2.0"
}

据我所知, jq 无法保留缩进,因此为了限制问题的范围,我只对如何保持不匹配版本的文件不受影响感兴趣。

我已经设法grep结合上面链接的问题中的答案来解决这个问题,但是是否有更有效的方法仅使用 jq ?如果可能的话,好处是保留原始文件的所有缩进。

find json \
    -name '*.json' \
    -type f -exec sh -c '
    for pathname do
        if grep "\"version\": \"2.0\"" "$pathname" 1> /dev/null; then
            tmpfile=$(mktemp)
            cp -- "$pathname" "$tmpfile" &&
            jq "select(.version == \"2.0\").identifier |= \"\"" <"$tmpfile" >"$pathname"
            rm -f "$tmpfile"
        fi
    done' sh {} +

答案1

grep在 JSON 文件上使用可能工作,但它依赖于文件的格式是否符合预期。另外,考虑到通用 JSON 文档中键的顺序不固定,identifier在识别 version 的同时测试键的非空值也很重要。2.0所以,是的,最好用于jq此目的。

任务只是触及需要更改的 JSON 文件。这些文件version具有值2.0identifier非空。

使用-e,jq可以以最后计算的表达式给出的退出状态退出,我们可以用它来测试当前文件是否要被修改。使用any(),我们可以检查任何选定的输入对象是否具有非空identifier值:

jq -e 'any(select(.version == "2.0"); .identifier != "")'

如果当前 JSON 文档需要修改,这将以零(“成功”)退出状态退出。

作为命令的一部分find

find json \
    -name '*.json' \
    -type f -exec sh -c '
    tmpfile=$(mktemp)
    for pathname do
        if jq -e "any(select(.version == \"2.0\"); .identifier != \"\")" "$pathname" >/dev/null
        then
            cp -- "$pathname" "$tmpfile" &&
            jq "select(.version == \"2.0\").identifier |= \"\"" <"$tmpfile" >"$pathname"
        fi
    done
    rm -f "$tmpfile"' sh {} +

请注意,任何文件第二次调用改变jq重写,这意味着除了identifier键的值之外,它可能会更改文件中的缩进和其他空格。从解析器的角度来看,这不会影响 JSON 文档,但可能会触发不支持 JSON 的工具报告文件的进一步更改。

如果您想编写带有四个缩进空格的 JSON,请添加--indent 4jq.

相关内容