我正在尝试使用 jq 修改相当大量的文件如果该文件包含特定值的属性:
find . \
-name '*.configuration.json' \
-type f -exec bash -c 'jq "select(.version == \"2.0\") | .identifier = \"\"" $0 | sponge "$0"' {} \;
所以我在这里学到了很多东西,比如如何实际重定向输出-exec
以及如何不是使用重定向输出jq
。
上面的脚本适用于找到的某些文件,但对于其他文件,它只是覆盖整个文件,什么也不做。我猜这是由竞争条件引起的,但我不知道如何解决这个问题。
任何帮助或指出我可能遇到的其他陷阱将不胜感激。
答案1
您的表达式的问题jq
在于,如果verison
JSON 对象的键不是2.0
,则不会选择该对象并且不会输出该对象。这意味着您实际上删除了任何不具有2.0
as 的内容version
。
相反,更新identifier
每个对象的值,其version
为2.0
:
jq 'select(.version == "2.0").identifier = ""'
关键是更新值而不从其对象中提取它。
人们还可以使用类似以下的内容,这更像是您代码中的内容:
jq 'select(.version == "2.0") |= (.identifier = "")'
这使用更新运算|=
符 来更新 所选择的对象select()
。
和find
:
find . -name '*.configuration.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 {} +
这也避免了不必要地多次调用内联脚本,并且sh -c
脚本中的循环确保保留原始文件的权限和所有权。
或者,如果您想使用sponge
(我自己从未使用过,所以请将此视为未经测试的示例):
find . -name '*.configuration.json' -type f -exec sh -c '
for pathname do
jq "select(.version == \"2.0\").identifier = \"\"" "$pathname" |
sponge "$pathname"
done' sh {} +