jqとfindを使用して前提条件に一致するファイルのみを編集する

jqとfindを使用して前提条件に一致するファイルのみを編集する

プロパティとバージョンの異なる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

grepJSONファイルでは、次のものを使用できます。働くしかし、予想される形式のファイルによって異なります。また、一般的な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 44つのスペースをインデントしてJSONを作成するにはjq

関連情報