Retina, 159 bytes
Byte count assumes ISO 8859-1 encoding. The leading linefeed is significant.
99$*1 bottles of beer
1
1$'W, 1$'.¶Take one down and pass it around, $'W.¶¶
1+
$.&
G-3`
W
on the wall
T`s``\b1 .+
.+ \B
Go to the store and buy some more, 99
I can't be believe I (or anyone else) have ever done this in Retina...
Explanation
99$* bottles of beer
We start by replacing the empty input with 99 1
s followed by bottles of beer
.
1
1$'W, 1$'.¶Take one down and pass it around, $'W.¶¶
Now we replace each of those 1
s with the substitution pattern on the second line. Here $'
stands for the string after each match, and we use W
as a placeholder for later. Note that the occurrences of $'
on the first line are "incremented" by prepending a 1
. We've got most of the work done already, except that the numbers are in unary instead of decimal, and we've got W
s instead of on the wall
s and the last verse is off.
1+
$.&
This does the unary-to-decimal conversion by matching each run of 1
s and replacing it with its length.
G-3`
This is a "grep" stage. The regex is empty, so it always matches, but the -3
limit means that only lines up to the third from the end are kept, so the last two are discarded. This gets rid of the two extraneous linefeeds at the end.
W
on the wall
Now we substitute our W
placeholder. At this point, we've got everything in place except that the last verse is still wrong.
T`s``\b1 .+
This fixes the pluralisation by removing all s
s from the match of the regex at the end, which matches anything after a 1
(on the same line).
Note that the last line doesn't have 0
since the unary representation of it is an empty string it wasn't matched by the conversion stage. Instead there will be two spaces in a row.
.+ \B
Go to the store and buy some more, 99
This is the only case where there is no word boundary after a space, so we detect the line like this and replace it with the correct lyrics.