Home > front end >  sed process multiple lines in one pass
sed process multiple lines in one pass

Time:09-17

I have two lines in the file like below:

#foo:
#   bar=<need replacement>

I'd like to remove the # if it exists(uncomment the line) for the two lines when the first line starting with foo and the second line contains bar and replace the text between <>.

Can I do this in one pass with N;P;D cycle using sed? Or is there any other simpler way to do it?

CodePudding user response:

This might work for you (GNU sed):

sed -E '/^#?foo/{N;/\n.*bar/{s/^#//mg;s/(\n.*<).*(>.*)/\1replace\2/};P;D}' file

If the current line begins with foo commented or uncommented, then:

  • Append the following line

  • If the following line contains the word bar, then:

    • Delete any leading # in either of the two lines.
    • Replace any characters between < and > on the second line with replace.
  • Print/delete the first of the two lines and repeat.

N.B. This allows a leading foo followed by another leading foo followed by bar to be processed.

CodePudding user response:

Using a POSIX with a standard N;…;P;D; loop and basic regexes (BRE):

sed -e '$!N' -e '/^#\(foo:\n\)#\([[:blank:]]*bar\)=.*/ s//\1\2=newvalue/' -e P -e D < file

CodePudding user response:

A piped sed may be needed if your data contains multiple lines of the text to be matched. e.g:

cat input_file
#foo:
#   bar=<need replacement>
#bar:
#  foo=<dont replace>
#foo:
#  bin=<dont replace>
#foo:
#  bar=<need replacement>
#foo:
# bash=<dont replace>

The first sed will deal with the condition of matching #foo, if it exist, then it will check the second condition that a bar matches on the following line. If both conditions are met, the n will then substitute # from the match on the line that matched bar and will also change the contents of need replacement to test.

The second sed pipe will use the same conditions, but rather than n to check and change the next line, I will use N to check the condition of the next line, but to change the first line if the condition is met and remove #

sed -E '/^#foo/ {n;/bar/ s/^#(.*=.).*(.)/\1test\2/g}' input_file | sed '/^#foo/ {N;/bar/ s/^#//g}'

Output

foo:
   bar=<test>
#bar:
#  foo=<dont replace>
#foo:
#  bin=<dont replace>
foo:
  bar=<test>
#foo:
# bash=<dont replace>

CodePudding user response:

It can probably be done, but I would use a different tool. How about Awk?

awk -v replace="new value" 'm && /^[^:] :/ { m=0 }
  /^#foo:/ { m=1 }
  m { sub(/^#/, ""); s(/<[^<>]*>/, replace) }1' 

This follows the familiar pattern of setting a state variable m when we see the beginning of the interesting region, performing the replacements when we are in the region, and turning off the variable when we see the beginning of a new region (speculating somewhat on what the condition for that might look like).

It's not hard to alter this to simply look for a fixed number of lines after the start of the region; set the state variable to a number, and decrement it for each line.

If this is a Makefile, using a make variable would be a lot more elegant and robust, though.

ifdef ENABLE
foo:
    bar=$(barvalue)
endif

(or perhaps simply ifdef barvalue!)

  • Related