jsonの外観の変更/シェルスクリプト内のjsonの変更(末尾のカンマを削除)

jsonの外観の変更/シェルスクリプト内のjsonの変更(末尾のカンマを削除)

私はこれのために多くの検索をしてきましたが、この必要性の前兆はないようです。

シェルスクリプトの一部として、プログラムでアプリケーション設定ファイルを編集する必要があります。

そしてデフォルト設定は厳密なjson形式で保存されます。つまり、,閉じる中括弧の前にカンマがあると、対応する設定ファイルをロードするアプリケーションが起動時にクラッシュします}

通常、これは問題ではありません。

私はsedそれに応じてsを使います。エラーテキストを含む行がサンプルファイルのセクションの末尾にある場合、このテキストを置き換えるときいつもカンマはありません。

交換しようとしている他の欠陥のあるビットを含む他の行が最後にない場合、いつもカンマを含む変更します。

例:

_(置換する文字列が時々バックスラッシュでいっぱいになるため、sedの区切り記号としてアンダースコアを使用します)

sed -i 's_"executableDecorator".*_"executableDecorator": "'$user_path'/faf/run \\"%s\\"",_' $user_path/.faforever/client.prefs

行が最後にある場合:

sed -i 's_"executableDecorator".*_"executableDecorator": "'$user_path'/faf/run \\"%s\\""_' $user_path/.faforever/client.prefs

これ会議それでも動作します! ...

両方とも同時にデフォルト設定を編集しないようにスクリプトを実行する前にアプリケーションを終了しましたが、それでもなお、私のスクリプトが受け取るデフォルト設定はアプリケーションの非同期実行によって毎回異なります。

これは完全にランダムです。

時には1行が途中にあることも、時には最後にあることもあります。アプリケーション自体(Javaと一部のjson java lib)は、コンテキストに応じてコンマを追加する方法を知っていますが、シェルスクリプトの一部として...何かが膨らんでいるような気がします。

(そうでない場合は、次の行があるかどうかに応じてカンマがあるかどうかを確認する速記があります。}これは私がもっと興味を持っているより簡単な解決策です。)

しかし、現在では、シェルスクリプトのすべての作業を終えた後にjson prefsファイルを「クリーンアップ」できるようにjsonを変更するPOSIXユーティリティを探しています...

編集する:

デフォルトファイル(フルファイル)は次のとおりです。

{
  "mainWindow": {
    "width": 800,
    "height": 600,
    "maximized": false,
    "lastView": "NEWS",
    "lastChildViews": {},
    "x": 67.0,
    "y": 27.0
  },
  "forgedAlliance": {
    "customMapsDirectory": "/home/t/My Games/Gas Powered Games/Supreme Commander Forged Alliance/Maps",
    "preferencesFile": "/home/t/.wine/drive_c/users/t/Application Data/Gas Powered Games/Supreme Commander Forged Alliance/Game.prefs",
    "officialMapsDirectory": "/home/t/faf/./Maps",
    "modsDirectory": "/home/t/My Games/Gas Powered Games/Supreme Commander Forged Alliance/Mods",
    "port": 6112,
    "autoDownloadMaps": true,
    "executableDecorator": "\"%s\""
  },
  "login": {
    "username": "tatsu",
    "password": "*******",
    "autoLogin": true
  },
  "chat": {
    "zoom": 1.0,
    "learnedAutoComplete": false,
    "previewImageUrls": true,
    "maxMessages": 500,
    "chatColorMode": "CUSTOM",
    "channelTabScrollPaneWidth": 250,
    "userToColor": {},
    "hideFoeMessages": true,
    "timeFormat": "AUTO",
    "chatFormat": "COMPACT",
    "idleThreshold": 10
  },
  "notification": {
    "soundsEnabled": true,
    "transientNotificationsEnabled": true,
    "mentionSoundEnabled": true,
    "infoSoundEnabled": true,
    "warnSoundEnabled": true,
    "errorSoundEnabled": true,
    "friendOnlineToastEnabled": true,
    "friendOfflineToastEnabled": true,
    "ladder1v1ToastEnabled": true,
    "friendOnlineSoundEnabled": true,
    "friendOfflineSoundEnabled": true,
    "friendJoinsGameSoundEnabled": true,
    "friendPlaysGameSoundEnabled": true,
    "friendPlaysGameToastEnabled": true,
    "privateMessageSoundEnabled": true,
    "privateMessageToastEnabled": true,
    "friendJoinsGameToastEnabled": true,
    "notifyOnAtMentionOnlyEnabled": false,
    "afterGameReviewEnabled": true,
    "toastPosition": "BOTTOM_RIGHT",
    "toastScreen": 0,
    "toastDisplayTime": 5000
  },
  "themeName": "default",
  "lastGameType": "faf",
  "localization": {},
  "rememberLastTab": true,
  "showPasswordProtectedGames": true,
  "showModdedGames": true,
  "ignoredNotifications": [],
  "lastGameMinRating": 800,
  "lastGameMaxRating": 1300,
  "ladder1v1": {
    "factions": [
      "aeon",
      "cybran",
      "uef",
      "seraphim"
    ]
  },
  "news": {
    "lastReadNewsUrl": "http://direct.faforever.com/2019/03/king-of-badlands-tournament-march-30th/"
  },
  "developer": {
    "gameRepositoryUrl": "https://github.com/FAForever/fa.git"
  },
  "vaultPrefs": {
    "onlineReplaySortConfig": {
      "sortProperty": "startTime",
      "sortOrder": "DESC"
    },
    "mapSortConfig": {
      "sortProperty": "statistics.plays",
      "sortOrder": "DESC"
    },
    "modVaultConfig": {
      "sortProperty": "latestVersion.createTime",
      "sortOrder": "DESC"
    }
  },
  "gameListSorting": [],
  "gameTileSortingOrder": "PLAYER_DES",
  "unitDataBaseType": "RACKOVER",
  "storedCookies": {},
  "lastGameOnlyFriends": false
}

唯一重要な部分は次のとおりです"forgedAlliance"

  "forgedAlliance": {
    "customMapsDirectory": "/home/t/My Games/Gas Powered Games/Supreme Commander Forged Alliance/Maps",
    "preferencesFile": "/home/t/.wine/drive_c/users/t/Application Data/Gas Powered Games/Supreme Commander Forged Alliance/Game.prefs",
    "officialMapsDirectory": "/home/t/faf/./Maps",
    "modsDirectory": "/home/t/My Games/Gas Powered Games/Supreme Commander Forged Alliance/Mods",
    "port": 6112,
    "autoDownloadMaps": true,
    "executableDecorator": "\"%s\""
  },

私はこれを得るために次のコマンドを実行します。

  "forgedAlliance": {
    "path": "/home/t/.steam/steam/steamapps/common/Supreme Commander Forged Alliance",
    "installationPath": "/home/t/.steam/steam/steamapps/common/Supreme Commander Forged Alliance",
    "customMapsDirectory": "/home/t/My Games/Gas Powered Games/Supreme Commander Forged Alliance/Maps",
    "preferencesFile": "/home/t/.steam/steam/steamapps/compatdata/9420/pfx/drive_c/users/steamuser/Local Settings/Application Data/Gas Powered Games/Supreme Commander Forged Alliance/Game.prefs",
    "officialMapsDirectory": "/home/t/faf/./Maps",
    "modsDirectory": "/home/t/My Games/Gas Powered Games/Supreme Commander Forged Alliance/Mods",
    "port": 6112,
    "autoDownloadMaps": true,
    "executableDecorator": "/home/t/faf/run \"%s\""
  },

有効なコマンド(標準の場合はオブジェクトが移動しない)は次のとおりです。

if ! grep -q '"path"' $user_path/.faforever/client.prefs > /dev/null
then
    sed -i '12i"path": "'$user_path'/.steam/steam/steamapps/common/Supreme Commander Forged Alliance",' $user_path/.faforever/client.prefs
    sed -i '13i"installationPath": "'$user_path'/.steam/steam/steamapps/common/Supreme Commander Forged Alliance",' $user_path/.faforever/client.prefs
fi
! grep -q '"preferencesFile": "'$user_path'/.steam/steam/steamapps/compatdata/9420/pfx/drive_c/users/steamuser/Local Settings/Application Data/Gas Powered Games/Supreme Commander Forged Alliance/Game.prefs",' $user_path/.faforever/client.prefs > /dev/null && sed -i 's_"preferencesFile".*_"preferencesFile": "'$user_path'/.steam/steam/steamapps/compatdata/9420/pfx/drive\_c/users/steamuser/Local Settings/Application Data/Gas Powered Games/Supreme Commander Forged Alliance/Game.prefs",_' $user_path/.faforever/client.prefs
! grep -q '"executableDecorator": "'$user_path'/faf/",' $user_path/.faforever/client.prefs > /dev/null && sed -i 's_"executableDecorator".*_"executableDecorator": "'$user_path'/faf/run \\"%s\\""_' $user_path/.faforever/client.prefs

答え1

これjqコマンド次の変更が適用されます。

jq --arg user_path "$user_path" '
    .forgedAlliance += {
        installationPath: ($user_path + "/.steam/steam/steamapps/common/Supreme Commander Forged Alliance"),
        path: ($user_path + "/.steam/steam/steamapps/common/Supreme Commander Forged Alliance"),
        preferencesFile: ($user_path + "/.steam/steam/steamapps/compatdata/9420/pfx/drive_c/users/steamuser/Local Settings/Application Data/Gas Powered Games/Supreme Commander Forged Alliance/Game.prefs"),
        executableDecorator: ($user_path + "/faf/run \"%s\"")
    }'

これは以下を使用します。

  1. --arg user_path "$user_path"シェル変数をjqプログラムにインポートする(あなたも利用できます変数バインディング演算子 "'"$user_path"'" as $user_path |しかし、これには醜い引用結合が含まれます)
  2. ファイル全体を処理するように割り当てを更新し、.forgedAlliance +=「forgedAlliance」キー値のみを更新します。マージする右側に内容があります。
  3. 新鮮なもの生成されたオブジェクトは次から{次に移動します。}その中には、計算したい新しいキー値のみを含めてください。同じ名前の既存のキーがある場合は置き換えられます。
  4. $user_path上記で作成した変数バインディングにアクセスします。

スペースはオプションです。ただサイトを読みやすくするためです。

jqは常に有効なJSONを出力するため、カンマ削除を実行する必要はありません。あなたは見つけることができますspongemoreutilsのコマンドjqにはそのファイルがないため、ファイル自体を更新するのに便利ですが、-i他のファイルにリダイレクトすることもできます。

jq ... > tmpfile
mv tmpfile prefs.json

手動でバイパスしてください。

コードで何が起こっているのか(少し?)違うのは、ファイルのどこにも「パス」が表示されたときに何もpath変更しないことです。installationPathjqを使用してコマンドを直接複製する方法はありませんが、必要なセマンティクス要素がある場合は、コマンドを2つの部分(パス用と常に部分用)に分割できます。このコマンドはいつも変更します。しかし、すでに同じキー値がある場合は効果がありません。


これが固定の代替セットの場合は、上記の3項目のオブジェクトのみを含むファイルを作成し(動的に計算されていない実際のJSON)、次のものを使用することもできます。

jq --slurpfile tmp rhs.json '.forgedAlliance += tmp[0]'

効果は上記のbigコマンドと同じです。

答え2

文字列置換の場合、下のプレースホルダA、Bに文字列を配置し、
Aのすべての/を\ /にエスケープし、〜を_の代わりにsedサブ区切り文字として使用します。例:
A -> "executableDecorator":
(B 新しく挿入された文字列)

sed -E '/A/{N;/A.*\n\s*\}/ {s~(A).*\n~\1B\n~;b} ;s~(A).*\n~\1B,\n~ }' B.json

いくつかの拡張例

sed -E '/"executableDecorator":/{N;/"executableDecorator":.*\n\s*\}/ {s~("executableDecorator":).*\n~\1B\n~;b} ;s~("executableDecorator":).*\n~\1B,\n~ }' B.json

関連情報