Reputation: 927
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.
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" :
{
"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.
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
However it seems to be quite complex and here are my questions.
Upvotes: 2
Views: 1824
Reputation: 290105
Parsing a JSON is not a very good idea (you should give a look to jq
), but awk
can help.
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
$ 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
}
}
Upvotes: 1
Reputation: 204164
sed is for simple substitutions on individual lines, that is all. For anything else you should be using 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:
if ($1 == "\""map[i]"\":") {
match($0,/: [^ ,]+/)
$0 = substr($0,1,RSTART-1) ": "map[i+1] substr($0,RSTART+RLENGTH)
}
Upvotes: 1
Reputation: 10039
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
Upvotes: 1