5

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
5

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

|improve this answer|||||
  • 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. – CompSci-PVT May 24 '16 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 – don_crissti May 24 '16 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 – CompSci-PVT May 24 '16 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. – don_crissti May 24 '16 at 14:44
8
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
#bar

Or:

line1 foo
line2 foo
#bar

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 '
  :1
  /foo$/ {
     n
     s/^#bar/bar/
     b1
  }'
|improve this answer|||||
  • Is there any reason you didn't use P;D for one line only solution? – cuonglm May 24 '16 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. – Stéphane Chazelas May 24 '16 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}' – Cacahuete Frito Feb 7 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). – Stéphane Chazelas Feb 7 at 14:02
2

Try:

sed -e '$!N;/foo\n#bar/s/\(\n\)#/\1/;P;D'
|improve this answer|||||
  • Using this, I get an error: sed: -e expression #1, char 35: unknown option to 's' – CompSci-PVT May 24 '16 at 14:00
  • @CompSci-PVT: Missing the ;, updated. – cuonglm May 24 '16 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. – CompSci-PVT May 24 '16 at 14:10
  • @CompSci-PVT: Check you line ends with foo or foo with trailing spaces? – cuonglm May 24 '16 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.