Using sed, how do I search for a line ending with foo and then edit the next line if it starts with #bar?

Or put another way, I want to remove a comment # from the next line if it starts with #bar and the previous line ends in foo.

For example:

This is a line ending in foo
#bar is commented out
There are many lines ending in foo
#bar commented out again

I tried:

sed -i 's/^foo\n#bar/foo\nbar/' infile

3 Answers 3

sed '/foo$/{n;s/^#bar/bar/;}'

is a literal translation of your requirement. n is for next.

Now that doesn't work in cases like:

line1 foo
#bar line2 foo


line1 foo
line2 foo

As the line that is pulled into the pattern space by n is not searched for foo.

You could address it by looping back to the beginning after the next line has been pulled into the pattern space:

sed '
  /foo$/ {
  • Is there any reason you didn't use P;D for one line only solution?
    – cuonglm
    May 24, 2016 at 15:09
  • 2
    @cuonglm, it looks like the P;D approaches are already covered in other answers. This answer shows the direct translation of the requirement approach (search for foo$, get the next line, substitute) along with its limitations. May 24, 2016 at 15:17
  • Excelent solution(s). Could you explain why the first one fails in those special cases, and why the second one works? BTW: a one line equivalent to the second one would be: sed ':1;/foo$/{n;s/^#bar/bar/;b1}' Feb 7, 2020 at 13:56
  • 1
    @CacahueteFrito, that wouldn't be portable/standard. You can't have anything after :1 or b1 because in many sed implementations including the original UNIX implementation, that anything would be taken as part the branching label's name (that even used to be required by POSIX). Feb 7, 2020 at 14:02

Use the N;P;D cycle and attempt to substitute each time:

sed '$!N;s/\(foo\n\)#\(bar\)/\1\2/;P;D' infile

this removes the leading # from #bar only if it follows a line ending in foo otherwise it just prints the pattern space unmodified.

Apparently, you want to uncomment US mirrors in /etc/pacman.d/mirrorlist which is a whole different thing:

sed -e '/United States/,/^$/{//!s/^#//' -e '}' /etc/pacman.d/mirrorlist

This will uncomment all mirrors in the US section in /etc/pacman.d/mirrorlist

  • This prints the lines, but it is not removing the '#'. It may be my implementation. I am using Arch Linux and am attempting to remove all the comments from the United States servers in the mirrorlist file. May 24, 2016 at 14:09
  • It does for me (archlinux too) so there's something wrong with your file. Maybe you have spaces, try with sed '$!N;s/\(foo[[:blank:]]*\n\)#\([[:blank:]]*bar\)/\1\2/;P;D' infile May 24, 2016 at 14:11
  • I was looking at this website and I commented out all the servers, and then wanted to only uncomment United States servers: wiki.archlinux.org/index.php/Mirrors#Sorting_mirrors May 24, 2016 at 14:26
  • @CompSci-PVT - lol, that's a whole different question dude... You want to uncomment all commented lines that follow a pattern inside a paragraph. See my updated post though you should edit the question to reflect your actual requirements. May 24, 2016 at 14:44


sed -e '$!N;/foo\n#bar/s/\(\n\)#/\1/;P;D'
  • Using this, I get an error: sed: -e expression #1, char 35: unknown option to 's' May 24, 2016 at 14:00
  • @CompSci-PVT: Missing the ;, updated.
    – cuonglm
    May 24, 2016 at 14:05
  • I am sure your answer is supposed to work, but it is not removing the '#' on my machine. Same as above answer. I will have to investigate more. May 24, 2016 at 14:10
  • @CompSci-PVT: Check you line ends with foo or foo with trailing spaces?
    – cuonglm
    May 24, 2016 at 14:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .