I'm looking for a one-liner to execute in the terminal to replace a multiline text block with my own context inside a text file. I'm on OSX (not GNU sed) and not able to install any additonal tools.
我正在寻找在终端中执行的一行程序,以在文本文件中使用我自己的上下文替换多行文本块。我使用的是OSX(不是GNU sed),无法安装任何附加工具。
What I want to do is to replace in
我想做的是代替!
{
"user" :
{
"name": "Andreas",
"age": 34
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
two lines between the curly brackets inside the "user" block with own values to get the result:
在“user”块内的花括号之间的两行具有自己的值以获得结果:
{
"user" :
{
"name": "Mike",
"age": 29
},
"viewer" :
{
"name": "Pedro",
"age": 41
}
}
Simple search for the lines containing "name" or "age" would not work as they can belong to another structure and should not be modified.
简单地搜索包含“name”或“age”的行是行不通的,因为它们可以属于另一个结构,不应该修改。
By combining several examples I found I got this one working:
通过结合几个例子,我发现这一个是可行的:
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
i ' -n $'1h;1!H;$ {;g;s #“用户”[^ {]*[^ }]* #“用户”:\ \ \ n \ \ \ t { \ \ \ n \ \ \ \ \ \ t“名称”:“迈克”,\ \ \ n \ \ \ \ \ \ t“年龄”:29 \ \ \ n \ \ \ t # p;}的json
However it seems to be quite complex and here are my questions.
然而,它似乎相当复杂,以下是我的问题。
- How the matching pattern can be modified to detect only the content between the brackets, so I don't have to recreate the "user" key.
- 如何修改匹配模式,只检测括号之间的内容,这样我就不必重新创建“user”键。
- Is there another more elegant solution? sed, awk or any other system tools inlcuded in OS X are welcome.
- 还有其他更优雅的解决方案吗?sed、awk或任何其他在OS X中嵌入的系统工具都是受欢迎的。
3 个解决方案
#1
1
Parsing a JSON is not a very good idea (you should give a look to jq
), but awk
can help.
解析JSON不是一个好主意(您应该看看jq),但是awk可以有所帮助。
For example, you can check when user
appears and, from there, act on the subsequent lines:
例如,您可以检查用户何时出现,然后从那里开始执行以下操作:
awk '/user/ {f=NR}
NR==f+2 {sub ("Andreas","Mike")}
NR==f+3 {sub (34, 29)}
1' file
You can also provide the new values as parameters.
还可以将新值作为参数提供。
If you don't know the value of the parameters, use a regular expression to match the content inside:
如果您不知道参数的值,请使用正则表达式来匹配内部内容:
awk '/user/ {f=NR} NR==f+2 {sub (/: ".*,$/,": \"Mike\",")} NR==f+3 {sub (/: [0-9]+$/, ": 29,")} 1' a
Test
$ 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
}
}
#2
1
sed -i '' -e '1h;1!H;$!d;x;s/\("user" :[^}]*"name": \)"[^"]*"\([^}]*"age": \)[0-9]*/\1"Mike"\234/' config.json
try this but cannot be sure there is not the same structure inside another one. It replace the first occurence
尝试一下,但是不能确定在另一个结构中没有相同的结构。它取代了第一次出现
#3
1
sed is for simple substitutions on individual lines, that is all. For anything else you should be using awk.
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 ($1 == "\""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
}
}
The above will fail if the replacement string contains &
since it's being used as the 2nd arg to sub()
- if that could happen then you'd use match()
and substr()
instead of sub()
so the replacement text is treated as a literal string:
如果替换字符串包含&因为它被用作第二个arg to sub()——如果可能发生,那么您将使用match()和substr()代替sub(),因此替换文本被当作一个字符串:
if ($1 == "\""map[i]"\":") {
match($0,/: [^ ,]+/)
$0 = substr($0,1,RSTART-1) ": "map[i+1] substr($0,RSTART+RLENGTH)
}
#1
1
Parsing a JSON is not a very good idea (you should give a look to jq
), but awk
can help.
解析JSON不是一个好主意(您应该看看jq),但是awk可以有所帮助。
For example, you can check when user
appears and, from there, act on the subsequent lines:
例如,您可以检查用户何时出现,然后从那里开始执行以下操作:
awk '/user/ {f=NR}
NR==f+2 {sub ("Andreas","Mike")}
NR==f+3 {sub (34, 29)}
1' file
You can also provide the new values as parameters.
还可以将新值作为参数提供。
If you don't know the value of the parameters, use a regular expression to match the content inside:
如果您不知道参数的值,请使用正则表达式来匹配内部内容:
awk '/user/ {f=NR} NR==f+2 {sub (/: ".*,$/,": \"Mike\",")} NR==f+3 {sub (/: [0-9]+$/, ": 29,")} 1' a
Test
$ 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
}
}
#2
1
sed -i '' -e '1h;1!H;$!d;x;s/\("user" :[^}]*"name": \)"[^"]*"\([^}]*"age": \)[0-9]*/\1"Mike"\234/' config.json
try this but cannot be sure there is not the same structure inside another one. It replace the first occurence
尝试一下,但是不能确定在另一个结构中没有相同的结构。它取代了第一次出现
#3
1
sed is for simple substitutions on individual lines, that is all. For anything else you should be using awk.
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 ($1 == "\""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
}
}
The above will fail if the replacement string contains &
since it's being used as the 2nd arg to sub()
- if that could happen then you'd use match()
and substr()
instead of sub()
so the replacement text is treated as a literal string:
如果替换字符串包含&因为它被用作第二个arg to sub()——如果可能发生,那么您将使用match()和substr()代替sub(),因此替换文本被当作一个字符串:
if ($1 == "\""map[i]"\":") {
match($0,/: [^ ,]+/)
$0 = substr($0,1,RSTART-1) ": "map[i+1] substr($0,RSTART+RLENGTH)
}