Reshaping json / reparing json inside shell script (remove trailing comma) Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern) 2019 Community Moderator Election Results Why I closed the “Why is Kali so hard” questionfind statement inside backticks not recognised inside shell scriptRemove lines in a shell scriptWrite permissions inside shell scriptShell script taking me inside the serversource inside a shell scriptReplace the comma with vertical bar |, except when inside double quotes, and remove double quotesRemoval of double quotes and substitution of comma inside itlooping through JSON array in shell scriptshell script inside dockerUse line break in JSON inside a shell script

Complexity of many constant time steps with occasional logarithmic steps

How to market an anarchic city as a tourism spot to people living in civilized areas?

How can players take actions together that are impossible otherwise?

Can I add database to AWS RDS MySQL without creating new instance?

Why does tar appear to skip file contents when output file is /dev/null?

What is the electric potential inside a point charge?

Did the new image of black hole confirm the general theory of relativity?

How to say that you spent the night with someone, you were only sleeping and nothing else?

Stars Make Stars

Who can trigger ship-wide alerts in Star Trek?

Simulating Exploding Dice

Problem when applying foreach loop

Can a non-EU citizen traveling with me come with me through the EU passport line?

Geometric mean and geometric standard deviation

Autumning in love

Jazz greats knew nothing of modes. Why are they used to improvise on standards?

Fishing simulator

Why is "Captain Marvel" translated as male in Portugal?

When is phishing education going too far?

How many things? AとBがふたつ

What can I do if my MacBook isn’t charging but already ran out?

How to rotate it perfectly?

Why use gamma over alpha radiation?

How to say 'striped' in Latin



Reshaping json / reparing json inside shell script (remove trailing comma)



Announcing the arrival of Valued Associate #679: Cesar Manara
Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern)
2019 Community Moderator Election Results
Why I closed the “Why is Kali so hard” questionfind statement inside backticks not recognised inside shell scriptRemove lines in a shell scriptWrite permissions inside shell scriptShell script taking me inside the serversource inside a shell scriptReplace the comma with vertical bar |, except when inside double quotes, and remove double quotesRemoval of double quotes and substitution of comma inside itlooping through JSON array in shell scriptshell script inside dockerUse line break in JSON inside a shell script



.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








3















I've googled around a lot for this and it seems there is no precursor to this need.



I need to edit an applications preference file programatically : as part of a shell script.



and the prefs are stored in strict json format : this means the app loading that preference file will crash at startup if there is a comma , before a closing curly brace }.



normally this wouldn't be an issue.



I'd just use my seds accordingly : if the line containing my faulty text lines up at the end of a section in my example file, then when replacing this text I will always put it without a comma.



If another line containing another faulty bit I want to replace is not at the end, I always replace it including a comma.



Example :



(I use underscore _ as my sed's delimiter because the stings to replace are full of backslashes sometimes)



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


if that line was at the end :



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


this would work, But!...



I have the app end before I run my script so that both aren't editing the preferences at the same time, but even still, because of this app's asynchronous execution the preferences my script will be receiving will differ every time.



it's completely random.



sometimes a line could be in the middle sometimes at the end. The app itself (Java & some json java lib) knows how to append comma or not depending on the context but as part of my shell script... I feel things are going to get bloated.



(If not and there's a shorthand to ensure I have comma or not depending on if next line is }, then that is a better simpler solution that I would be more interested in)



But as it stands I'm looking for a POSIX utility that fixes json so that I can "sanitize" my json prefs file as soon as I'm done butchering it all within my shell script ...does such a thing exist?



EDIT :



here's the base file (whole file, ) :




"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



the only part that matters is "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""
,


I run commands to obtain this :



 "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""
,


the commands that work (in a standard case where things don't move around) are :



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









share|improve this question
























  • Is there no way at all you could use jq or similar to modify it in the first place instead of text-mangling?

    – Michael Homer
    Mar 31 at 20:29











  • surely! what's jq? googling...

    – tatsu
    Mar 31 at 20:30











  • In that case, if you could provide a minimal example of the file structure and the changes you'd like to make, someone can probably steer you right.

    – Michael Homer
    Mar 31 at 20:31











  • Ok, I added the file, plus the bit I edit, plus the result and also what I used to get there.

    – tatsu
    Mar 31 at 20:38

















3















I've googled around a lot for this and it seems there is no precursor to this need.



I need to edit an applications preference file programatically : as part of a shell script.



and the prefs are stored in strict json format : this means the app loading that preference file will crash at startup if there is a comma , before a closing curly brace }.



normally this wouldn't be an issue.



I'd just use my seds accordingly : if the line containing my faulty text lines up at the end of a section in my example file, then when replacing this text I will always put it without a comma.



If another line containing another faulty bit I want to replace is not at the end, I always replace it including a comma.



Example :



(I use underscore _ as my sed's delimiter because the stings to replace are full of backslashes sometimes)



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


if that line was at the end :



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


this would work, But!...



I have the app end before I run my script so that both aren't editing the preferences at the same time, but even still, because of this app's asynchronous execution the preferences my script will be receiving will differ every time.



it's completely random.



sometimes a line could be in the middle sometimes at the end. The app itself (Java & some json java lib) knows how to append comma or not depending on the context but as part of my shell script... I feel things are going to get bloated.



(If not and there's a shorthand to ensure I have comma or not depending on if next line is }, then that is a better simpler solution that I would be more interested in)



But as it stands I'm looking for a POSIX utility that fixes json so that I can "sanitize" my json prefs file as soon as I'm done butchering it all within my shell script ...does such a thing exist?



EDIT :



here's the base file (whole file, ) :




"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



the only part that matters is "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""
,


I run commands to obtain this :



 "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""
,


the commands that work (in a standard case where things don't move around) are :



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









share|improve this question
























  • Is there no way at all you could use jq or similar to modify it in the first place instead of text-mangling?

    – Michael Homer
    Mar 31 at 20:29











  • surely! what's jq? googling...

    – tatsu
    Mar 31 at 20:30











  • In that case, if you could provide a minimal example of the file structure and the changes you'd like to make, someone can probably steer you right.

    – Michael Homer
    Mar 31 at 20:31











  • Ok, I added the file, plus the bit I edit, plus the result and also what I used to get there.

    – tatsu
    Mar 31 at 20:38













3












3








3








I've googled around a lot for this and it seems there is no precursor to this need.



I need to edit an applications preference file programatically : as part of a shell script.



and the prefs are stored in strict json format : this means the app loading that preference file will crash at startup if there is a comma , before a closing curly brace }.



normally this wouldn't be an issue.



I'd just use my seds accordingly : if the line containing my faulty text lines up at the end of a section in my example file, then when replacing this text I will always put it without a comma.



If another line containing another faulty bit I want to replace is not at the end, I always replace it including a comma.



Example :



(I use underscore _ as my sed's delimiter because the stings to replace are full of backslashes sometimes)



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


if that line was at the end :



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


this would work, But!...



I have the app end before I run my script so that both aren't editing the preferences at the same time, but even still, because of this app's asynchronous execution the preferences my script will be receiving will differ every time.



it's completely random.



sometimes a line could be in the middle sometimes at the end. The app itself (Java & some json java lib) knows how to append comma or not depending on the context but as part of my shell script... I feel things are going to get bloated.



(If not and there's a shorthand to ensure I have comma or not depending on if next line is }, then that is a better simpler solution that I would be more interested in)



But as it stands I'm looking for a POSIX utility that fixes json so that I can "sanitize" my json prefs file as soon as I'm done butchering it all within my shell script ...does such a thing exist?



EDIT :



here's the base file (whole file, ) :




"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



the only part that matters is "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""
,


I run commands to obtain this :



 "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""
,


the commands that work (in a standard case where things don't move around) are :



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









share|improve this question
















I've googled around a lot for this and it seems there is no precursor to this need.



I need to edit an applications preference file programatically : as part of a shell script.



and the prefs are stored in strict json format : this means the app loading that preference file will crash at startup if there is a comma , before a closing curly brace }.



normally this wouldn't be an issue.



I'd just use my seds accordingly : if the line containing my faulty text lines up at the end of a section in my example file, then when replacing this text I will always put it without a comma.



If another line containing another faulty bit I want to replace is not at the end, I always replace it including a comma.



Example :



(I use underscore _ as my sed's delimiter because the stings to replace are full of backslashes sometimes)



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


if that line was at the end :



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


this would work, But!...



I have the app end before I run my script so that both aren't editing the preferences at the same time, but even still, because of this app's asynchronous execution the preferences my script will be receiving will differ every time.



it's completely random.



sometimes a line could be in the middle sometimes at the end. The app itself (Java & some json java lib) knows how to append comma or not depending on the context but as part of my shell script... I feel things are going to get bloated.



(If not and there's a shorthand to ensure I have comma or not depending on if next line is }, then that is a better simpler solution that I would be more interested in)



But as it stands I'm looking for a POSIX utility that fixes json so that I can "sanitize" my json prefs file as soon as I'm done butchering it all within my shell script ...does such a thing exist?



EDIT :



here's the base file (whole file, ) :




"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



the only part that matters is "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""
,


I run commands to obtain this :



 "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""
,


the commands that work (in a standard case where things don't move around) are :



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






shell-script shell sed posix






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 31 at 20:38







tatsu

















asked Mar 31 at 20:25









tatsutatsu

1237




1237












  • Is there no way at all you could use jq or similar to modify it in the first place instead of text-mangling?

    – Michael Homer
    Mar 31 at 20:29











  • surely! what's jq? googling...

    – tatsu
    Mar 31 at 20:30











  • In that case, if you could provide a minimal example of the file structure and the changes you'd like to make, someone can probably steer you right.

    – Michael Homer
    Mar 31 at 20:31











  • Ok, I added the file, plus the bit I edit, plus the result and also what I used to get there.

    – tatsu
    Mar 31 at 20:38

















  • Is there no way at all you could use jq or similar to modify it in the first place instead of text-mangling?

    – Michael Homer
    Mar 31 at 20:29











  • surely! what's jq? googling...

    – tatsu
    Mar 31 at 20:30











  • In that case, if you could provide a minimal example of the file structure and the changes you'd like to make, someone can probably steer you right.

    – Michael Homer
    Mar 31 at 20:31











  • Ok, I added the file, plus the bit I edit, plus the result and also what I used to get there.

    – tatsu
    Mar 31 at 20:38
















Is there no way at all you could use jq or similar to modify it in the first place instead of text-mangling?

– Michael Homer
Mar 31 at 20:29





Is there no way at all you could use jq or similar to modify it in the first place instead of text-mangling?

– Michael Homer
Mar 31 at 20:29













surely! what's jq? googling...

– tatsu
Mar 31 at 20:30





surely! what's jq? googling...

– tatsu
Mar 31 at 20:30













In that case, if you could provide a minimal example of the file structure and the changes you'd like to make, someone can probably steer you right.

– Michael Homer
Mar 31 at 20:31





In that case, if you could provide a minimal example of the file structure and the changes you'd like to make, someone can probably steer you right.

– Michael Homer
Mar 31 at 20:31













Ok, I added the file, plus the bit I edit, plus the result and also what I used to get there.

– tatsu
Mar 31 at 20:38





Ok, I added the file, plus the bit I edit, plus the result and also what I used to get there.

– tatsu
Mar 31 at 20:38










2 Answers
2






active

oldest

votes


















6














This jq command will make exactly those changes:



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"")
'


This uses




  1. --arg user_path "$user_path" to bring the shell variable into the jq program (you could also use the variable binding operator "'"$user_path"'" as $user_path |, but it would involve ugly quote splicing)

  2. Update-assignment .forgedAlliance += to process the whole file, updating just the value of the "forgedAlliance" key by merging it with what's on the right.

  3. A fresh object constructed from to with just the new key values you wanted computed inside it. If there are existing keys with the same name, they will be replaced.


  4. $user_path to access that variable binding we made above.

The whitespace is optional - it's just there to make it easier to read on this site.



jq always outputs as valid JSON, so you don't have any comma cleanup to do. You may find the sponge command from moreutils useful for updating the file itself, because there is no -i equivalent in jq, but you can also just redirect to another file



jq ... > tmpfile
mv tmpfile prefs.json


and step around it manually as well.



There is one (slight?) difference to what your code did: you made no changes for path and installationPath if "path" appeared anywhere in the file. There's no way to replicate that with jq directly, but you could split the command in two (one for path, one for all the time) if there's a necessary semantic element to that. This command will always make the change, but if it's already got the same value for a key that doesn't have any effect.




If this is a fixed set of replacements, you could also make a file with just the object from point 3 above in it literally (as true JSON, not dynamically computed), and then use



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


with the same effect as the big command above.






share|improve this answer























  • "you made no changes for path and installationPath if "path" appeared anywhere in the file.", well looking back on that that's terrible. I prefer your approach. it does mean installing jq will have to become part of the script but maybe that's a manageable sacrifice.

    – tatsu
    Mar 31 at 23:11



















0














for strings substitute, put your strings in placeholder A, B below,

escape any / in A with /, and ~ as sed sub separator rather _

E.g. A -> "executableDecorator":

(B your new inserted string)



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


some expansion instance;



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





share|improve this answer























    Your Answer








    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "106"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f509764%2freshaping-json-reparing-json-inside-shell-script-remove-trailing-comma%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    6














    This jq command will make exactly those changes:



    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"")
    '


    This uses




    1. --arg user_path "$user_path" to bring the shell variable into the jq program (you could also use the variable binding operator "'"$user_path"'" as $user_path |, but it would involve ugly quote splicing)

    2. Update-assignment .forgedAlliance += to process the whole file, updating just the value of the "forgedAlliance" key by merging it with what's on the right.

    3. A fresh object constructed from to with just the new key values you wanted computed inside it. If there are existing keys with the same name, they will be replaced.


    4. $user_path to access that variable binding we made above.

    The whitespace is optional - it's just there to make it easier to read on this site.



    jq always outputs as valid JSON, so you don't have any comma cleanup to do. You may find the sponge command from moreutils useful for updating the file itself, because there is no -i equivalent in jq, but you can also just redirect to another file



    jq ... > tmpfile
    mv tmpfile prefs.json


    and step around it manually as well.



    There is one (slight?) difference to what your code did: you made no changes for path and installationPath if "path" appeared anywhere in the file. There's no way to replicate that with jq directly, but you could split the command in two (one for path, one for all the time) if there's a necessary semantic element to that. This command will always make the change, but if it's already got the same value for a key that doesn't have any effect.




    If this is a fixed set of replacements, you could also make a file with just the object from point 3 above in it literally (as true JSON, not dynamically computed), and then use



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


    with the same effect as the big command above.






    share|improve this answer























    • "you made no changes for path and installationPath if "path" appeared anywhere in the file.", well looking back on that that's terrible. I prefer your approach. it does mean installing jq will have to become part of the script but maybe that's a manageable sacrifice.

      – tatsu
      Mar 31 at 23:11
















    6














    This jq command will make exactly those changes:



    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"")
    '


    This uses




    1. --arg user_path "$user_path" to bring the shell variable into the jq program (you could also use the variable binding operator "'"$user_path"'" as $user_path |, but it would involve ugly quote splicing)

    2. Update-assignment .forgedAlliance += to process the whole file, updating just the value of the "forgedAlliance" key by merging it with what's on the right.

    3. A fresh object constructed from to with just the new key values you wanted computed inside it. If there are existing keys with the same name, they will be replaced.


    4. $user_path to access that variable binding we made above.

    The whitespace is optional - it's just there to make it easier to read on this site.



    jq always outputs as valid JSON, so you don't have any comma cleanup to do. You may find the sponge command from moreutils useful for updating the file itself, because there is no -i equivalent in jq, but you can also just redirect to another file



    jq ... > tmpfile
    mv tmpfile prefs.json


    and step around it manually as well.



    There is one (slight?) difference to what your code did: you made no changes for path and installationPath if "path" appeared anywhere in the file. There's no way to replicate that with jq directly, but you could split the command in two (one for path, one for all the time) if there's a necessary semantic element to that. This command will always make the change, but if it's already got the same value for a key that doesn't have any effect.




    If this is a fixed set of replacements, you could also make a file with just the object from point 3 above in it literally (as true JSON, not dynamically computed), and then use



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


    with the same effect as the big command above.






    share|improve this answer























    • "you made no changes for path and installationPath if "path" appeared anywhere in the file.", well looking back on that that's terrible. I prefer your approach. it does mean installing jq will have to become part of the script but maybe that's a manageable sacrifice.

      – tatsu
      Mar 31 at 23:11














    6












    6








    6







    This jq command will make exactly those changes:



    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"")
    '


    This uses




    1. --arg user_path "$user_path" to bring the shell variable into the jq program (you could also use the variable binding operator "'"$user_path"'" as $user_path |, but it would involve ugly quote splicing)

    2. Update-assignment .forgedAlliance += to process the whole file, updating just the value of the "forgedAlliance" key by merging it with what's on the right.

    3. A fresh object constructed from to with just the new key values you wanted computed inside it. If there are existing keys with the same name, they will be replaced.


    4. $user_path to access that variable binding we made above.

    The whitespace is optional - it's just there to make it easier to read on this site.



    jq always outputs as valid JSON, so you don't have any comma cleanup to do. You may find the sponge command from moreutils useful for updating the file itself, because there is no -i equivalent in jq, but you can also just redirect to another file



    jq ... > tmpfile
    mv tmpfile prefs.json


    and step around it manually as well.



    There is one (slight?) difference to what your code did: you made no changes for path and installationPath if "path" appeared anywhere in the file. There's no way to replicate that with jq directly, but you could split the command in two (one for path, one for all the time) if there's a necessary semantic element to that. This command will always make the change, but if it's already got the same value for a key that doesn't have any effect.




    If this is a fixed set of replacements, you could also make a file with just the object from point 3 above in it literally (as true JSON, not dynamically computed), and then use



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


    with the same effect as the big command above.






    share|improve this answer













    This jq command will make exactly those changes:



    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"")
    '


    This uses




    1. --arg user_path "$user_path" to bring the shell variable into the jq program (you could also use the variable binding operator "'"$user_path"'" as $user_path |, but it would involve ugly quote splicing)

    2. Update-assignment .forgedAlliance += to process the whole file, updating just the value of the "forgedAlliance" key by merging it with what's on the right.

    3. A fresh object constructed from to with just the new key values you wanted computed inside it. If there are existing keys with the same name, they will be replaced.


    4. $user_path to access that variable binding we made above.

    The whitespace is optional - it's just there to make it easier to read on this site.



    jq always outputs as valid JSON, so you don't have any comma cleanup to do. You may find the sponge command from moreutils useful for updating the file itself, because there is no -i equivalent in jq, but you can also just redirect to another file



    jq ... > tmpfile
    mv tmpfile prefs.json


    and step around it manually as well.



    There is one (slight?) difference to what your code did: you made no changes for path and installationPath if "path" appeared anywhere in the file. There's no way to replicate that with jq directly, but you could split the command in two (one for path, one for all the time) if there's a necessary semantic element to that. This command will always make the change, but if it's already got the same value for a key that doesn't have any effect.




    If this is a fixed set of replacements, you could also make a file with just the object from point 3 above in it literally (as true JSON, not dynamically computed), and then use



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


    with the same effect as the big command above.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Mar 31 at 21:15









    Michael HomerMichael Homer

    51k8141178




    51k8141178












    • "you made no changes for path and installationPath if "path" appeared anywhere in the file.", well looking back on that that's terrible. I prefer your approach. it does mean installing jq will have to become part of the script but maybe that's a manageable sacrifice.

      – tatsu
      Mar 31 at 23:11


















    • "you made no changes for path and installationPath if "path" appeared anywhere in the file.", well looking back on that that's terrible. I prefer your approach. it does mean installing jq will have to become part of the script but maybe that's a manageable sacrifice.

      – tatsu
      Mar 31 at 23:11

















    "you made no changes for path and installationPath if "path" appeared anywhere in the file.", well looking back on that that's terrible. I prefer your approach. it does mean installing jq will have to become part of the script but maybe that's a manageable sacrifice.

    – tatsu
    Mar 31 at 23:11






    "you made no changes for path and installationPath if "path" appeared anywhere in the file.", well looking back on that that's terrible. I prefer your approach. it does mean installing jq will have to become part of the script but maybe that's a manageable sacrifice.

    – tatsu
    Mar 31 at 23:11














    0














    for strings substitute, put your strings in placeholder A, B below,

    escape any / in A with /, and ~ as sed sub separator rather _

    E.g. A -> "executableDecorator":

    (B your new inserted string)



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


    some expansion instance;



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





    share|improve this answer



























      0














      for strings substitute, put your strings in placeholder A, B below,

      escape any / in A with /, and ~ as sed sub separator rather _

      E.g. A -> "executableDecorator":

      (B your new inserted string)



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


      some expansion instance;



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





      share|improve this answer

























        0












        0








        0







        for strings substitute, put your strings in placeholder A, B below,

        escape any / in A with /, and ~ as sed sub separator rather _

        E.g. A -> "executableDecorator":

        (B your new inserted string)



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


        some expansion instance;



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





        share|improve this answer













        for strings substitute, put your strings in placeholder A, B below,

        escape any / in A with /, and ~ as sed sub separator rather _

        E.g. A -> "executableDecorator":

        (B your new inserted string)



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


        some expansion instance;



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






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Apr 4 at 16:56









        abdanabdan

        594




        594



























            draft saved

            draft discarded
















































            Thanks for contributing an answer to Unix & Linux Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f509764%2freshaping-json-reparing-json-inside-shell-script-remove-trailing-comma%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Adding axes to figuresAdding axes labels to LaTeX figuresLaTeX equivalent of ConTeXt buffersRotate a node but not its content: the case of the ellipse decorationHow to define the default vertical distance between nodes?TikZ scaling graphic and adjust node position and keep font sizeNumerical conditional within tikz keys?adding axes to shapesAlign axes across subfiguresAdding figures with a certain orderLine up nested tikz enviroments or how to get rid of themAdding axes labels to LaTeX figures

            Luettelo Yhdysvaltain laivaston lentotukialuksista Lähteet | Navigointivalikko

            Gary (muusikko) Sisällysluettelo Historia | Rockin' High | Lähteet | Aiheesta muualla | NavigointivalikkoInfobox OKTuomas "Gary" Keskinen Ancaran kitaristiksiProjekti Rockin' High