如何使用jq正确替换JSON文件中重复出现56次的指定字符串
我们有如下json文件(以下为使用grep的匹配示例):
grep DB_SERVER_CV02.localdomain /home/main.json "hadoop.proxyuser.ambari.hosts": "DB_SERVER_CV02.localdomain", "yarn.timeline-service.http-authentication.proxyuser.ambari.hosts": "DB_SERVER_CV02.localdomain", "timeline.metrics.service.webapp.address": "DB_SERVER_CV02.localdomain:6188", "javax.jdo.option.ConnectionURL": "jdbc:postgresql://DB_SERVER_CV02.localdomain:5432/hive", "webhcat.proxyuser.ambari.hosts": "DB_SERVER_CV02.localdomain", . . . . .
DB_SERVER_CV02.localdomain字符串在main.json文件中总共出现了超过56次,我们需要将所有DB_SERVER_CV02.localdomain替换为APP_SERVER_CV02.localdomain。
最简单的实现方案是使用sed执行以下命令:
sed -i s'/DB_SERVER_CV02.localdomain/APP_SERVER_CV02.localdomain/g' /home/main.json
但众所周知处理json文件时使用jq更加规范可靠,请问使用jq实现上述全局替换的正确方案是什么?
正确实现方案
适用jq 1.6及以上版本(当前绝大多数发行版默认版本)
使用内置的walk函数递归遍历所有JSON节点,仅对字符串类型的节点执行全局替换,完全不会破坏JSON结构:
jq 'walk(if type == "string" then gsub("DB_SERVER_CV02\\.localdomain"; "APP_SERVER_CV02.localdomain") else . end)' /home/main.json > /home/main.json.tmp && mv /home/main.json.tmp /home/main.json
参数说明:
walk()会递归遍历JSON内所有层级的对象、数组、值节点- 仅对字符串类型的节点执行替换,不会误改数值、布尔值、空值等非字符串内容
gsub为jq的全局替换函数,原域名中的.是正则特殊字符,因此添加转义符\\确保精确匹配,避免误伤其他类似字符串- jq没有原生的原地修改参数,因此先输出到临时文件再替换原文件,避免操作异常导致原文件损坏
适用jq 1.5及更早版本
低版本jq没有内置walk函数,手动定义后执行相同逻辑即可:
jq 'def walk(f): . as $in | if type == "object" then reduce keys[] as $key ( {}; . + { ($key): ($in[$key] | walk(f)) } ) elif type == "array" then map( walk(f) ) else f end; walk(if type == "string" then gsub("DB_SERVER_CV02\\.localdomain"; "APP_SERVER_CV02.localdomain") else . end)' /home/main.json > /home/main.json.tmp && mv /home/main.json.tmp /home/main.json
和sed纯文本替换相比,上述方案只会修改JSON的值字段中的匹配内容,完全不会破坏JSON语法结构,也不会误替换键名、注释等其他位置的字符,稳定性更高。
内容的提问来源于stack exchange,提问作者jessica
相关产品推荐
相关产品推荐

