我有相当多的 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.0
且identifier
非空。
使用-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 4
到jq
.