プロパティとバージョンの異なるJSONファイルがかなりあります。特定の条件に一致するファイルのみを変更したいと思います。フォローアップです。findとjqを使用した内部編集。
何百ものファイルを修正してPRを開くと、変更したい以外は何も必要ないので、これは私にとって重要です。たとえば、インデントを変更すると、ファイル全体が変更されたように見え、レビュー担当者は実際の違いを確認するのが難しくなります。
何を意味するかを説明するために、次の2つのファイルを名前付きフォルダに配置します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番目のファイルには空の識別子(予想)があり、インデントは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
のNULL以外の値をテストすることも重要です。2.0
はい。jq
この目的に使用するのが最善です。
これは、変更が必要なJSONファイルにのみ適用されます。このファイルにはversion
値があり、空では2.0
ありidentifier
ません。
を使用すると、-e
最後jq
に評価された式によって提供された終了状態で終了でき、それを使用して現在のファイルが変更されるかどうかをテストできます。以下を使用して、any()
選択した入力オブジェクトにnull以外の値があるかどうかを確認できますidentifier
。
jq -e 'any(select(.version == "2.0"); .identifier != "")'
現在のJSON文書に変更が必要な場合、終了ステータスは0(「成功」)で終了します。
コマンドの一部として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 {} +
どんなファイルでも参照してくださいはい2番目の通話はjq
次のように変更されます。修正これは、identifier
キー値に加えてファイルのインデントやその他のスペースを変更できることを意味します。パーサーの観点からは、これはJSON文書には影響しませんが、JSONをサポートしていないツールによって報告されたファイルへの追加の変更を引き起こす可能性があります。
--indent 4
4つのスペースをインデントしてJSONを作成するにはjq
。