Home > front end >  Using sed - how to replace the first occurrence of a string on the line after the first occurrence o
Using sed - how to replace the first occurrence of a string on the line after the first occurrence o

Time:10-18

I have a bit of a layered problem with using sed to use a variable as a search string AND do this across multiple lines. I'm able to do either or, but not both simultaneously. I'm working in an xml file that looks something like this.

<tag property="search1">
        string
</tag>
<tag property="search2">
        string
</tag>
<tag property="search3">
        string
</tag>

I'm trying to sequentially replace "string" with another value depending on the number of the "search" string on the line before it, with a script. The script increments a counter to do this.

I can find and replace "string" after "search$n" if "$n" is already known:

$ sed '$!N;/search2/ s/\string/foo/;P;D' test
<tag property="search1">
        string
</tag>
<tag property="search2">
        foo
</tag>
<tag property="search3">
        string
</tag>

And I can replace strings based on a variable search:

$ n=2 
$ sed "/search$n/ s/search/foo/" test
<tag property="search1">
        string
</tag>
<tag property="foo2">
        string
</tag>
<tag property="search3">
        string
</tag>

But I haven't been able to figure out how to combine the two:

$ sed '$!N;/search$n/ s/\string/foo/;P;D' test

The above command works; as in it doesn't throw an error, but it doesn't parse the variable - I've tried escaping it, and also putting it in double or single quotes and escaping those. The arguments that allow me to parse multiple lines in sed seem to require single quotes, while reading variables in the search field requires double quotes...

I'm on OSX and using gnu-sed. Here are some of the other things I've tried:

sed "/search$n/, 1s/string/foo/" test
sed '/search$n/, 1s/string/foo/' test
sed "/search$n/, 1s s/string/foo/" test
sed '/search$n/, 1 s/string/foo/' test
sed '' -e '/search$n/ {' -e 'n; s/string/foo/' -e '}' test 
sed '' -e '/search$n/ {' -e 'n; s/.*/foo/' -e '}' test 
sed '/search$n/!b;n;c/foo/' test 
sed '' -e '/search$n/!b;n;string' test 
sed '' -e "/search$n/ {' -e 'n; s/string/foo/' -e '}" test 
sed '' -e "/search$n/ {' -e 'n; s/.*/foo/g' -e '}" test 
sed '' -e "/search$n/ s/string/foo/" test 
sed -e "/search$n/ s/string/foo/" test 
sed "/search$n/ s/string/foo/" test 

CodePudding user response:

You need to declare n=2 (not i=2) and then use double quotes to allow variable expnasion.

However, you need to take care of $ and ! that are special to Bash. You can use

n=2
sed '$!'"N;/search$n/ s/string/foo/;P;D" test

Output:

<tag property="search1">
        string
</tag>
<tag property="search2">
        foo
</tag>
<tag property="search3">
        string
</tag>

The '$!'"N;/search$n/ s/string/foo/;P;D" is a concatenation of $! (without variable expansion support) and N;/search$n/ s/string/foo/;P;D (with variable expansion support).

CodePudding user response:

This might work for you (GNU sed):

n=2
sed '/search'"$n"'/{n;s/string/foo/}' file

Set n to 2.

Match on search2, print the current line and fetch the next.

If the following line contains string replace string by foo.

It is possible that the following line does not contain string but contains search2, in which case:

sed ':a;/search'"$n"'/{n;s/string/foo/;Ta}' file
  • Related