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

이것회의그래도 작동합니다! ...

둘 다 동시에 기본 설정을 편집하지 않도록 스크립트를 실행하기 전에 애플리케이션을 종료했지만, 그럼에도 불구하고 내 스크립트가 받는 기본 설정은 애플리케이션의 비동기 실행으로 인해 매번 달라집니다.

이것은 완전히 무작위입니다.

때로는 한 줄이 중간에 있을 수도 있고 때로는 끝에 있을 수도 있습니다. 애플리케이션 자체(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를 사용하여 명령을 직접 복제할 수 있는 방법은 없지만 필요한 의미 체계 요소가 있는 경우 명령을 두 부분(경로용 부분과 항상 부분용)으로 나눌 수 있습니다. 이 명령은언제나변경합니다. 하지만 이미 동일한 키 값이 있으면 아무런 효과가 없습니다.


이것이 고정된 대체 세트인 경우 위의 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

관련 정보