我已经为此进行了很多搜索,似乎没有这种需求的先兆。
我需要以编程方式编辑应用程序首选项文件:作为 shell 脚本的一部分。
并且首选项以严格的 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)知道如何根据上下文附加逗号或不附加逗号,但作为我的 shell 脚本的一部分......我觉得事情会变得臃肿。
(如果没有,并且有一个简写来确保我有逗号或没有逗号,具体取决于下一行是否是}
,那么这是一个更好的更简单的解决方案,我会更感兴趣)
但就目前情况而言,我正在寻找一个修复 json 的 POSIX 实用程序,以便在我完成 shell 脚本中的所有操作后,我可以“清理”我的 json prefs 文件……这样的东西存在吗?
编辑 :
这是基本文件(整个文件):
{
"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\"")
}'
这使用
--arg user_path "$user_path"
将shell变量带入jq程序中(您也可以使用变量绑定运算符"'"$user_path"'" as $user_path |
,但这会涉及丑陋的引号拼接)- 更新分配
.forgedAlliance +=
来处理整个文件,仅更新“forgedAlliance”键的值合并它与右边的内容。 - 一个新鲜的构造的对象从
{
到}
仅包含您想要在其中计算的新键值。如果存在同名的现有密钥,它们将被替换。 $user_path
访问我们上面所做的变量绑定。
空格是可选的 - 它只是为了让网站更容易阅读。
jq 始终输出为有效的 JSON,因此您无需进行任何逗号清理。您可能会发现sponge
来自 moreutils 的命令对于更新文件本身很有用,因为 jq 中没有-i
等效项,但您也可以重定向到另一个文件
jq ... > tmpfile
mv tmpfile prefs.json
并手动绕过它。
与您的代码所做的有一个(轻微?)差异:如果“路径”出现在文件中的任何位置path
,则您没有进行任何更改。installationPath
无法直接使用 jq 复制该命令,但如果有必要的语义元素,您可以将命令分成两部分(一个用于路径,一个用于所有时间)。该命令将总是进行更改,但如果它已经具有相同的键值,则不会产生任何效果。
如果这是一组固定的替换,您还可以创建一个文件,其中仅包含上面第 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