如何用 sed、awk 或其他 OS X 工具替换文件(JSON 格式)中的多行块?
How to replace a multiline block from a file (JSON format) with sed, awk or other OS X tools?
我正在寻找在终端中执行的单行代码,以在文本文件中用我自己的上下文替换多行文本块。我在 OSX(不是 GNU sed)上,无法安装任何附加工具。
我想做的是在
中替换
{
"user" :
{
"name": "Andreas",
"age": 34
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
"user" 块内的大括号之间的两行具有自己的值以获得结果:
{
"user" :
{
"name": "Mike",
"age": 29
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
简单搜索包含 "name" 或 "age" 的行将不起作用,因为它们可能属于另一个结构,不应修改。
通过结合几个示例,我发现我可以使用这个示例:
sed -i '' -n $'1h;1! H;$ {;g;s#"user"[^{]*[^}]*#"user" :\\n\\t{\\n\\t\\t"name": "Mike",\\n\\t\\t"age": 29\\n\\t#p;}' config.json
然而它似乎相当复杂,这是我的问题。
- 如何修改匹配模式以仅检测括号之间的内容,这样我就不必重新创建 "user" 键。
- 还有其他更优雅的方案吗?欢迎使用 sed、awk 或 OS X 中包含的任何其他系统工具。
解析 JSON 不是一个好主意(你应该看看 jq
),但 awk
可以提供帮助。
例如,您可以检查 user
何时出现,并从那里开始对后续行进行操作:
awk '/user/ {f=NR}
NR==f+2 {sub ("Andreas","Mike")}
NR==f+3 {sub (34, 29)}
1' file
您还可以提供新值作为参数。
如果不知道参数的值,用正则表达式匹配里面的内容:
awk '/user/ {f=NR} NR==f+2 {sub (/: ".*,$/,": \"Mike\",")} NR==f+3 {sub (/: [0-9]+$/, ": 29,")} 1' a
测试
$ awk '/user/ {f=NR} NR==f+2 {sub ("Andreas","Mike")} NR==f+3 {sub (34, 29)} 1' a
{
"user" :
{
"name": "Mike",
"age": 29
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
sed -i '' -e '1h;1!H;$!d;x;s/\("user" :[^}]*"name": \)"[^"]*"\([^}]*"age": \)[0-9]*/"Mike"4/' config.json
试试这个,但不能确定另一个结构中是否有相同的结构。它取代了第一次出现的
sed 用于在单行上进行简单替换,仅此而已。对于其他任何事情,您应该使用 awk。
$ cat tst.awk
BEGIN { split("name \"Mike\" age 29",map) }
/"user"/ { inUser = 1 }
inUser {
for (i=1;i in map;i+=2) {
if ( == "\""map[i]"\":") {
sub(/: [^ ,]+/,": "map[i+1])
}
}
if (/}/) {
inUser = 0
}
}
{ print }
$
$ awk -f tst.awk file
{
"user" :
{
"name": "Mike",
"age": 29
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
如果替换字符串包含 &
,上面将失败,因为它被用作 sub()
的第二个参数 - 如果可能发生,那么您将使用 match()
和 substr()
而不是 sub()
因此替换文本被视为文字字符串:
if ( == "\""map[i]"\":") {
match([=11=],/: [^ ,]+/)
[=11=] = substr([=11=],1,RSTART-1) ": "map[i+1] substr([=11=],RSTART+RLENGTH)
}
我正在寻找在终端中执行的单行代码,以在文本文件中用我自己的上下文替换多行文本块。我在 OSX(不是 GNU sed)上,无法安装任何附加工具。
我想做的是在
中替换{
"user" :
{
"name": "Andreas",
"age": 34
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
"user" 块内的大括号之间的两行具有自己的值以获得结果:
{
"user" :
{
"name": "Mike",
"age": 29
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
简单搜索包含 "name" 或 "age" 的行将不起作用,因为它们可能属于另一个结构,不应修改。
通过结合几个示例,我发现我可以使用这个示例:
sed -i '' -n $'1h;1! H;$ {;g;s#"user"[^{]*[^}]*#"user" :\\n\\t{\\n\\t\\t"name": "Mike",\\n\\t\\t"age": 29\\n\\t#p;}' config.json
然而它似乎相当复杂,这是我的问题。
- 如何修改匹配模式以仅检测括号之间的内容,这样我就不必重新创建 "user" 键。
- 还有其他更优雅的方案吗?欢迎使用 sed、awk 或 OS X 中包含的任何其他系统工具。
解析 JSON 不是一个好主意(你应该看看 jq
),但 awk
可以提供帮助。
例如,您可以检查 user
何时出现,并从那里开始对后续行进行操作:
awk '/user/ {f=NR}
NR==f+2 {sub ("Andreas","Mike")}
NR==f+3 {sub (34, 29)}
1' file
您还可以提供新值作为参数。
如果不知道参数的值,用正则表达式匹配里面的内容:
awk '/user/ {f=NR} NR==f+2 {sub (/: ".*,$/,": \"Mike\",")} NR==f+3 {sub (/: [0-9]+$/, ": 29,")} 1' a
测试
$ awk '/user/ {f=NR} NR==f+2 {sub ("Andreas","Mike")} NR==f+3 {sub (34, 29)} 1' a
{
"user" :
{
"name": "Mike",
"age": 29
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
sed -i '' -e '1h;1!H;$!d;x;s/\("user" :[^}]*"name": \)"[^"]*"\([^}]*"age": \)[0-9]*/"Mike"4/' config.json
试试这个,但不能确定另一个结构中是否有相同的结构。它取代了第一次出现的
sed 用于在单行上进行简单替换,仅此而已。对于其他任何事情,您应该使用 awk。
$ cat tst.awk
BEGIN { split("name \"Mike\" age 29",map) }
/"user"/ { inUser = 1 }
inUser {
for (i=1;i in map;i+=2) {
if ( == "\""map[i]"\":") {
sub(/: [^ ,]+/,": "map[i+1])
}
}
if (/}/) {
inUser = 0
}
}
{ print }
$
$ awk -f tst.awk file
{
"user" :
{
"name": "Mike",
"age": 29
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
如果替换字符串包含 &
,上面将失败,因为它被用作 sub()
的第二个参数 - 如果可能发生,那么您将使用 match()
和 substr()
而不是 sub()
因此替换文本被视为文字字符串:
if ( == "\""map[i]"\":") {
match([=11=],/: [^ ,]+/)
[=11=] = substr([=11=],1,RSTART-1) ": "map[i+1] substr([=11=],RSTART+RLENGTH)
}