text:
"99 Bottles of Beer"
Write a program that outputs the lyrics to 99 Bottles of Beer, in as few bytes as possible.
Lyrics:
99 bottles of beer on the wall, 99 bottles of beer.
Take one down and pass it around, 98 bottles of beer on the wall.

98 bottles of beer on the wall, 98 bottles of beer.
Take one down and pass it around, 97 bottles of beer on the wall.

97 bottles of beer on the wall, 97 bottles of beer.
Take one down and pass it around, 96 bottles of beer on the wall.

96 bottles of beer on the wall, 96 bottles of beer.
Take one down and pass it around, 95 bottles of beer on the wall.

95 bottles of beer on the wall, 95 bottles of beer.
Take one down and pass it around, 94 bottles of beer on the wall.

....

3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.

2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.

1 bottle of beer on the wall, 1 bottle of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.

Rules:

Your program must log to STDOUT or an acceptable alternative, or be returned from a function (with or without a trailing newline).
Your program must be a full, runnable program or function.
Languages specifically written to submit a 0-byte answer to this challenge are allowed, just not particularly interesting.
Note that there must be an interpreter so the submission can be tested. It is allowed (and even encouraged) to write this interpreter yourself for a previously unimplemented language.
This is different from the output by HQ9+ or 99. Any answers written in these languages will be deleted.

As this is a catalog challenge, this is not about finding the language with the shortest solution for this (there are some where the empty program does the trick) - this is about finding the shortest solution in every language. Therefore, no answer will be marked as accepted.
Catalogue
The Stack Snippet at the bottom of this post generates the catalogue from the answers a) as a list of shortest solution per language and b) as an overall leaderboard.
To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:
## Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:
## Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:
## Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the snippet:
## [><>](http://esolangs.org/wiki/Fish), 121 bytes

 <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b">

Shortest Solution by Language

Language	User	Score

Leaderboard

	Author	Language	Size

{{PLACE}}	{{NAME}}	{{LANGUAGE}}	{{SIZE}}	Link

{{LANGUAGE}}	{{NAME}}	{{SIZE}}	Link

It can be a function but must output to STDOUT/closest alternative?

@CᴏɴᴏʀO'Bʀɪᴇɴ fixed.

Whoever closevoted this, the other one is a popularity contest...

This is a subtask of the Create an HQ9+ Interpreter golf.

@Geobits Yet it is distinct in that the focus is shifted; you need not read input; you may use a programming language made after the challenge; and that you may a function.

@CᴏɴᴏʀO'Bʀɪᴇɴ I haven't VTCd it yet, still deciding how I feel about that. Either way, I think some answers (or snippets from them) from the other can be directly copy/pasted while remaining competitive, which is a usual test for dupe-ness.

You might be interested in: http://www.99-bottles-of-beer.net

Note that quartata had already sandboxed a 99BB catalog several weeks ago and was planning to post it.

Other catalogue questions have stipulated that answer be in the form of a full program and not a function. Is there a reason for this change?

@dohaqatar7 Potentially the lack of sandboxing. ;) (I don't think there is anything wrong with a catalogue allowing functions though. It just seemed cleaner for I/O-focussed challenges to require full programs.)

@AlexA. I was not aware of this, my bad. If there is any way to transfer ownership of the question go ahead. Otherwise, what should I do to fix this predicament?

@GamrCorps No, there's no way to transfer ownership of a question other than to make it a community wiki, which would also make all of the answers CW. I think the only way to "fix" the predicament is to note it for next time. ;)

What is a "full runnable program or function" ? How is that different from a "program or function"?

@GamrCorps Please do not unnecessarily change the rules after the challenge has started. None of the characters in the output should be optional.

You didn't take the last bottle of beer down... and bought 98 bottles of beer after that.

Wait, who drinks that 1 bottle of beer? After 1 iteration you'd have 100 bottles not 99. If nobody drinks it that's alcohol abuse.

Some answers seem to print trailing whitespace. Is that allowed?

@Dennis yes, that is allowed.

This now has 99 answers!

@gcampbell No, it doesn't

@DmitryKudriavtsev It did when I wrote that.

Someone do this in the C++ Preprocessor Language with the rules listed here.

If you have a Tcl/Tk interpreter, the code on http://www.99-bottles-of-beer.net/language-tcl-tk-1468.html does a fun animation when pasted to the Wish interpreter window! http://i.imgur.com/dEaeVJF.png

To try and head off any newcomers who can't see deleted answers, if you're thinking of using a language with a 99 Bottles song built-in, double check the output matches the desired output of the question. There have already been half a dozen deleted HQ9+ answers, as well as other languages with incorrect outputs.

Are capital letters allowed?

C#, 285 298 289 Bytes
(My first attempt at Code golfing ...)
class d{static void Main(){for(int b=99;b>0;)System.Console.Write("{0}{6}{1}{2}, {0}{6}{1} of beer. {3}, {4}{6}{5}{2}.{7}",b,b==1?"":"s"," of beer on the wall",b==1?"Go to the store and buy some more":"Take one down and pass it around",b==1?99:b-1,b==2?"":"s"," bottle",b--<2?"":"\n\n");}}

A little bit ungolfed:
class d{
    static void Main(){
        for(int b = 99; b > 0;){
            System.Console.Write("{0}{6}{1}{2}, {0}{6}{1} of beer.\n{3}, {4}{6}{5}{2}.{7}", b, b==1 ? "" : "s", " of beer on the wall", b == 1 ? "Go to the store and buy some more" : "Take one down and pass it around", b == 1 ? 99 : b-1, b== 2 ? "" : "s", " bottle", b--<2 ? "" : "\n\n");
        }
    }
}

tip - use var instead of string :)

2 bytes from replacing " bottle" with a format argument though I'm sure there is a better way, having 3 instances of bottle hurts my eyes...

Good point. I already tried replacing "bottle" (no less bytes), but forgot that I could also replace " bottle".

b==1 can be replaced with b<2.

I think you can save 1 byte moving the b-- to the end (where you do your b==2 check). I think as in it works on ideone!

Your for loop could be written like this for(int b = 100; b--> 1;)

Why not use concatenation? This is code-golf, so not much focus is needed on performance. Or do I know the wrong thing?

Extra newline at the end...

@frnhr The trailing newline isn't a problem - nearly all of the top solutions have one. In certain languages, such as Python, it's nearly unavoidable.

Then maybe we should wait for the challenge author to decide if a trailing newline is allowed or not.

You can remove the declaration of s and use its output directly.

I know it's been almost 1.5 years, but you can golf all three occurrences of b==1 to b<2 for -3 bytes. Try it here. EDIT: As @LegionMammal978 had already mentioned on Nov. 18th, 2015..

Here is my take on the challenge

Using .NET 5 and C#9's top level statements, this can be dropped down to 264. Remove the class and Main blocks and you're chopped down.

Motorola MC14500B Machine Code, 46612 bytes
For length reasons, I can not post the program here. However, it can be found here in hexadecimal, and here in binary (padded with 0s).

This is the shortest possible program in Motorola MC14500B machine code. It consists of only 1000 and 1001 (8 and 9, respectively); one opcode for each bit of output.
It uses 93,224 half-byte opcodes, and outputs the song lyrics one bit at a time. This is the only possible output method.
For those interested, the output goes to pin #3 (of 16), the I/O pin.

Explanation
8    Store the register's value
9    Store the logical complement of the register's value

The register starts at 0.

Code Trivia

The hexadecimal is 93,224 8s and 9s long.
The binary is 745,792 1s and 0s long.
I used the Python 2 code below to generate the code. Input 0 for binary and 1 for hexadecimal.
a,b=["8","9"]if input()else["00001000","00001001"]
f="""99 Bottles of Beer lyrics"""
print''.join(b if int(i)else a for i in''.join((8-len(bin(i)[2:]))*'0'+bin(i)[2:]for i in bytearray(f)))

Please tell me you didn't just go and write this all out without some sort of string generator... :c

See the new "Code Trivia" section :P @VoteToClose

The links are broken :(

I'll fix it when I get back home. Sorry about that ): @ETHproductions

Vitsy, 0 Bytes

Seriously ain't got nothin' on me. (@Mego I'm so sorry. ;))
Try it online! (Just hit "Run")

o_o I need something interesting for Simplex to do in zero bytes :P

I remembered reading that some language had a 0 byte solution, but I couldn't remember which one. Well done.

I'm betting it's another language, @ThomasKwa. c:

@CᴏɴᴏʀO'Bʀɪᴇɴ Same with Japt :P

@CᴏɴᴏʀO'Bʀɪᴇɴ I think you mean, "I need something useful for Simplex to do in zero bytes."

@MartinBüttner Perhaps, yes. Though nothing comes to mind.

@VoteToClose - How does this work? I honestly don't know, I'm not a code golfer, but I enjoy coming here and seeing people and languages do it. But I can't comprehend this. +1 because it looks impressive!

@Jonnny Click here to see the interpreter's file handler. The file handler will see that a file contains zero bytes, and, if so, print out the lyrics. c:

@VoteToClose Oh! It's your own language - kudos!

@Jonnny Yeah - it's a work in progress, one day, it'll be better than Seriously. But for now... We wait. If you want a good golfing language, see the Seriously answer, it's got some crazy stuff in it.

@Jonnny Also, if you think you might be interested in my language, use is:answer Vitsy in the search bar to find some more interesting answers that are in this language. c:

This violates this standard loophole, since the commit that added this zero byte behaviour was made specifically for this challenge after it was posted

Seriously, 1 byte
N

If the stack is empty (which it is at the start), N pushes the lyrics. Then they're implicitly printed at EOF.
Thanks to @Mego for fixing the Seriously interpreter.

Martin said in the Hello World challenge, ">please don't downvote boring (but valid) answers in languages where there is not much to golf - these are still useful to this question as it tries to compile a catalogue as complete as possible. However, do primarily upvote answers in languages where the authors actually had to put effort into golfing the code." This answer is uninteresting, but it is a valid and the shortest solution.

When reading the question I couldn't wait to see you posting a Serious answer. But it looks like I don't even have to wait. :)

Seriusly????????

I look forward to seeing your solution without the use of N :)

Yeah - I have four down votes and my answer is valid. I concur with what is said in the top comment here.

Seriously? :3
(This length limitation is stupid)

Seriously? Oh wait 2 comments already made that joke

JavaScript ES6, 230 218 198 196 192 188 185 bytes
for(x=99,z=(a=' on the wall')=>`${x||99} bottle${1-x?'s':''} of beer`+a;x;)alert(z()+', '+z`.
`+(--x?'Take one down and pass it around, ':'Go to the store and buy some more, ')+z()+'.')

Just just trimming off a few bytes while still keeping it looking clean and understandable.
3 most recent revisions:
for(x=99,z=a=>`${x||99} bottle${1-x?'s':''} of beer${a||' on the wall'}`;x;)alert(z()+', '+z(`.
`)+(--x?'Take one down and pass it around, ':'Go to the store and buy some more, ')+z()+'.')

for(x=99,z=a=>(x||99)+' bottle'+(1-x?'s':'')+' of beer',w=' on the wall';x;)alert(z()+w+', '+z()+(--x?`.
Take one down and pass it around, `:`.
Go to the store and buy some more, `)+z()+w+'.')

for(x=99,o=' bottle',q=b=o+'s',e=' of beer',w=e+" on the wall";x;)alert(x+b+w+', '+x+b+e+(--x?`.
Take one down and pass it around, `+x:`.
Go to the store and buy some more, 99`)+(b=1-x?q:o)+w+'.')

2 bytes saved by putting the variables outside of the loop controls:x=99;w=" on the wall";e=" of beer";o=" bottle";b=o+"s";for(z="Go to the store and buy some more, "+x+b;0<x;)console.log(x+(2>x?o:b)+e+w+", "+x+(2>x?o:b)+e+".\n"+(1>--x?z:"Take one down and pass it around, "+x+(2>x?o:b))+e+w+".")

That's not what's saving those 2 bytes in your code. It's actually the removal of the parenthesis around (--x<1). For now I'll leave the variables inside the for statement for readability purposes to keep them all together.

It's only because s = 99, with s>99 the savings are canceled or negative. I guess if it gets close between mine and the next js answer then I'll have to make such drastic changes, ha ha.

@AdamDally +1 (You defeated C and Python 2) I found a silly way to save 1 more byte...look at my answer if you are interested. (Tip: Look at the function calls)

@Stefnotch If you are wondering, I found this edit by examining my function calls...but I don't think this was your intent. Your advice is still relevant as a last resort, though.

Does this show a pop-up for each verse?

@Paŭlo Ebermann It does execute alert for each verse, but you are free to change it to console.log to execute in the console, which would make it 194 bytes. Plus alerting the entire lyrics in one go doesn't work in Firefox, at least...it trims off a good 5-10 verses.

@AdamDally I really like the fact that you used ${1-x?'s':''}! I also used a very similar variant! :D (I came up with it separately, but it might be possible that I wouldn't even have thought of it if I hadn't taken a glance at your answer.)
Also, I outgolfed you! :D bragging

@Stefnotch. Well that 1-x is proving useful at the moment.

JavaScript ES6, 328 318 307 305 bytes
Is an anonymous function. Add f= at the beginning to make function and f() to execute.
x=>eval('s=o=>v=(o?o:" no more")+" bottle"+(1==o?"":"s");for(o="",i=99;i>0;)o+=`${s(i)}@ on the wall, ${v}@.\nTake one down, pass it around, ${s(--i)}@ on the wall.\n`;o+`No more bottles@ on the wall, no more bottles@.\nGo to the store and buy some more, 99 bottles@ on the wall.`'.replace(/@/g," of beer"))

Pretty would be a lot more interesting if it was more than just one long string concatenation

C, 197 196 bytes
main(i){for(i=299;i--/3;printf("%d bottle%s of beer%s%s",i/3?:99,"s"+5/i%2,i%3?" on the wall":i^3?".\nTake one down and pass it around":".\nGo to the store and buy some more",~i%3?", ":".\n\n"));}

I think I have reached the limit of this approach.

"%d bottles of beer on the wallTake one down and pass it aroundGo to the store and buy some more,.\n"main(){for(;;)printf();} alone is 126 bytes, so that represents a hard lower bound unless someone finds a way to compress or reuse parts of the strings without significant overhead.  I suspect you may have reached the limit of approaches in general, plus or minus a few bytes.

Java 304 301 300 295 Bytes
First time posting an answer. I heard we could use enum but could not find how.
interface A{static void main(String[]a){String b=" of beer",c=" on the wall",n=".\n",s;for(int i=100;i-->1;s=" bottle"+(i>1?"s":""),System.out.println(i+s+b+c+", "+i+s+b+n+(i<2?"Go to the store and buy some more, 99":"Take one down and pass it around, "+(i-1))+" bottle"+(i!=2?"s":"")+b+c+n));}}

Ungolfed
interface A {
    static void main(String[] a) {
        String b = " of beer", c = " on the wall", n = ".\n", s;
        for (int i = 100; i-- > 1; s = " bottle" + (i > 1 ? "s" : ""), System.out.println(i + s + b + c + ", " + i + s + b + n + (i < 2 ? "Go to the store and buy some more, 99" : "Take one down and pass it around, " + (i - 1)) + " bottle" + (i != 2 ? "s" : "") + b + c + n));
    }
}

Thanks to quartata, J Atkin and Benjamin Urquhart

You can save 2 bytes by using i<2 in place of i==1 and i>2 here: i!=2.

@JAtkin Thank you. For the first one it is ok, the output stays the same. But for the second one, if I make the change, the last line will print 99 bottle and not 99 bottles

Actually I had just noticed that, since I'm trying to rework your (very nice) code into groovy to see if its sorter than mine.

Thank you, please do. I was not certain of it was maxgolfed, since I'm new to this. :)

As it turns out, this is good for java, but this doesn't work well for groovy (213 vs 245 bytes).

@JAtkin Groovy is a golfed version for Java I suppose?

Not really, groovy is one of the JVM languages. Fo most applications groovy code is shorter than the java code that does the same thing. goovy-lang.org

if you put print statement inside for loop statement, then you can remove semicolon and curly brackets

4 years later, -5 bytes

@BenjaminUrquhart Great! Modified it !

We can save 1 byte by moving the -- to the end of the loop body (at the last i), then changing the 100 to a 99 and the 1 to a 0 in the loop initialization/condition.

We can save an additional 8 bytes (total 286 bytes) by putting " bottle" into a variable and moving the --, which allows us to change the (i-1) to i.

Building on @JeffreyH 285 bytes

Brainfuck, 743 bytes
++++++++++>>--->++++>------>-->>++>+>>+>->++[[>+++++++[>++>+>+<<<-]<-]+<+++]>>>>>--
>>-->>++>->+++[<]>[-<<+[<->-----]<---[<[<]>[->]<]+<[>-]>[-<++++++++++<->>>]-[<->---
--]<+++[<[<]>[+>]<]<->>>[>[[>]<<<<.<<<<---.>>>-.<<<++++.<.>>>.-.<<.<.>-.>>+.<+++.>-
.<<<.>---.>>.<<+++.<.>>>++.<<---.>----..<<.>>>>--.+++<<+.<<.>.>--.>-.<+++.->-.+<<++
+.<<.>.<<<<-<]>[[>]<<<-.<<<.<<<.>>.>.<<< . > > .<++++.---.<.>>-.+.>.<--.<.<.>----.>
>-.<<+++.<.>--.>+++.++++.<<.>>------.>+.--.<<+++.<.>>>.++.<-.++<.-<<.>.<<<<-<<<++++
+++++.>>>]<+>]<++[<<[<]>[.>]>>>>>>.>--.>>.<..->>.<<<+++.<<<<-[>>>>>.[<]]<[>]>+>>>.>
>>.<<+.<.>----.+++..->-.++[<<]<-[>>>>>.>>>.-.+<<<.>>.<++++.---.<.>>+++.---<----.+++
>>>..[<<]]<<[>]>-[>>>>++.--<.<<<+[>>>.<<<<]>[>]<-<]>>[>>.>.<<<<]>[<<++>]<[>]<-]>+>]

Try it online!
The above uses wrapping cells in a few places to save instructions. I've also made a non-wrapping version in 755 instructions.

Uncompressed
The following is a polyglot in Brainfuck Annotation Language.
; byte sequence
; \n _ _ _ _ c \n comma space d t o l T G
++++++++++>>
--->++++>------>-->>++>+>>+>->++
[[>+++++++[>++>+>+<<<-]<-]+<+++]
>>>>>-->>-->>++>->+++[<]

c = 0
n = 100

>
while(n)
[
  n = n minus 1
  -<<
  N = str(n)
  ; first time \n _ becomes 9 9
  +[<->-----]<---[<[<]>[->]<]
  +<[>-]>[-<++++++++++<->>>]
  -[<->-----]<+++[<[<]>[+>]<]

  <->>>
  if(c)
  [
    >
    if(n)
    [
      'Take one down and'
      [>]<<<<.<<<<---.>>>-.<<<++++.<.>>>.-.<<.<.>-.>>+.<+++.>-.<<<.>---.>>.<<+++.
      ' pass it around' comma space
      <.>>>++.<<---.>----..<<.>>>>--.+++<<+.<<.>.>--.>-.<+++.->-.+<<+++.<<.>.<<<<-<
    ]>
    else
    [
      'Go to the store and'
      [>]<<<-.<<<.<<<.>>.>.<<<.>>.<++++.---.<.>>-.+.>.<--.<.<.>----.>>-.<<+++.
      ' buy some more' comma space
      <.>--.>+++.++++.<<.>>------.>+.--.<<+++.<.>>>.++.<-.++<.-<<.>.<<<<-
      N = '9'
      N
      <<<+++++++++.>>>
    ]
    <+>
  ]

  c = c plus 2
  <++

  while(c)
  [
    N
    <<[<]>[.>]>>>
    ' bottle'
    >>>.>--.>>.<..->>.<<<+++.<<<<
    if(n minus 1) 's'
    -[>>>>>.[<]]<[>]>+
    ' of beer'
    >>>.>>>.<<+.<.>----.+++..->-.++

    [<<]<-
    if(c minus 1)
    [
      ' on the wall'
      >>>>>.>>>.-.+<<<.>>.<++++.---.<.>>+++.---<----.+++>>>..[<<]
    ]

    <<[>]>-
    if(c minus 2)
    [
      period newline
      >>>>++.--<.<<<+
      if(c minus 1) newline
      [>>>.<<<<]>[>]<-<
    ]>>
    else
    [
      comma space
      >>.>.<<<<
    ]

    if(not n) c = c minus 2
    >[<<++>]

    c = c minus 1
    <[>]<-
  ]

  c = 1
  >+>
]

Haskell, 228 223 bytes
o=" of beer on the wall"
a n=shows n" bottle"++['s'|n>1]
b 1="Go to the store and buy some more, "++a 99
b n="Take one down and pass it around, "++a(n-1)
f=[99,98..1]>>= \n->[a n,o,", ",a n," of beer.\n",b n,o,".\n\n"]>>=id

Function f returns a string with the lyrics.

Templates Considered Harmful, 667 bytes
Ap<Fun<Ap<Fun<Cat<Cat<Cat<Cat<Ap<A<1,1>,A<1>>,A<2,1>>,St<44,32>>,Ap<A<1,1>,A<1>>>,If<A<1>,Cat<Cat<Cat<Cat<St<46,10,84,97,107,101,32,111,110,101,32,100,111,119,110,32,97,110,100,32,112,97,115,115,32,105,116,32,97,114,111,117,110,100,44,32>,Ap<A<1,1>,Sub<A<1>,T>>>,A<2,1>>,St<46,10,10>>,Ap<A<0>,Sub<A<1>,T>>>,Cat<Cat<Cat<St<46,10,71,111,32,116,111,32,116,104,101,32,115,116,111,114,101,32,97,110,100,32,98,117,121,32,115,111,109,101,32,109,111,114,101,44,32>,Ap<A<1,1>,I<98>>>,A<2,1>>,St<46>>>>>,I<98>>>,Fun<Cat<Cat<Cat<Add<A<1>,T>,St<32,98,111,116,116,108,101>>,If<A<1>,St<'s'>,St<>>>,St<32,111,102,32,98,101,101,114>>>,St<32,111,110,32,116,104,101,32,119,97,108,108>>

Sort of expanded:
Ap<
    Fun<
        Ap<
            Fun<
                Cat<
                    Cat<Cat<Cat< Ap<A<1,1>,A<1>> , A<2,1> >, St<44,32> >, Ap<A<1,1>,A<1>> >,
                    If<A<1>,
                        Cat<Cat<Cat<Cat< St<46,10,84,97,107,101,32,111,110,101,32,100,111,119,110,32,97,110,100,32,112,97,115,115,32,105,116,32,97,114,111,117,110,100,44,32> , Ap<A<1,1>,Sub<A<1>,T>> >, A<2,1> >, St<46,10,10> >, Ap<A<0>,Sub<A<1>,T>> >,
                        Cat<Cat<Cat< St<46,10,71,111,32,116,111,32,116,104,101,32,115,116,111,114,101,32,97,110,100,32,98,117,121,32,115,111,109,101,32,109,111,114,101,44,32> , Ap<A<1,1>,I<98>> >, A<2,1> >, St<46> >
                    >
                >
            >,
            I<98>
        >
    >,
    Fun< Cat<Cat<Cat< Add<A<1>,T> , St<32,98,111,116,116,108,101> >, If<A<1>,St<'s'>,St<>>  >, St<32,111,102,32,98,101,101,114> > >,
    St<32,111,110,32,116,104,101,32,119,97,108,108>
>

///, 341 bytes
/-/\/\///+/ bottle-)/\/&\/<\/
-(/\/\/?\/ ->/+s of beer-^/> on the wall-!/^,-$/>.
-@/$Take one down and pass it around,-#/^.
-*/?1@?0#<0!?0@-%/99!?9@?8#<8!?8@?7#<7!?7@?6#<6!?6@?5#<5!?5@?4#<4!?4@?3#<3!?3@?2#<2!?2@?1#<1!-&/?9#
%*-</
9(9/%*/</
8(8)7(7)6(6)5(5)4(4)3(3)2(2)1(1)0(0-
0/
- 0/ /#/1+s/1+/
% 01$Go to the store and buy some more, 099^.

It would need 99 years to write a proper explanation of this code.
I would probably just include the result of every stage...
Basically, this compresses the lyrics repeatedly (as is every answer in ///).
Try it online!
Each step of decompression
Since replacements followed by replacements will have the string //, it will appear often.
It appears often enough that I decided to compress // into -.
When this is decompressed, the result is as follows:
/+/ bottle//)/\/&\/<\/
//(/\/\/?\/ //>/+s of beer//^/> on the wall//!/^,//$/>.
//@/$Take one down and pass it around,//#/^.
//*/?1@?0#<0!?0@//%/99!?9@?8#<8!?8@?7#<7!?7@?6#<6!?6@?5#<5!?5@?4#<4!?4@?3#<3!?3@?2#<2!?2@?1#<1!//&/?9#
%*//</
9(9/%*/</
8(8)7(7)6(6)5(5)4(4)3(3)2(2)1(1)0(0//
0/
// 0/ /#/1+s/1+/
% 01$Go to the store and buy some more, 099^.

The string  bottle only appeared three times, but I compressed it to + anyways:
/)/\/&\/<\/
//(/\/\/?\/ //>/ bottles of beer//^/> on the wall//!/^,//$/>.
//@/$Take one down and pass it around,//#/^.
//*/?1@?0#<0!?0@//%/99!?9@?8#<8!?8@?7#<7!?7@?6#<6!?6@?5#<5!?5@?4#<4!?4@?3#<3!?3@?2#<2!?2@?1#<1!//&/?9#
%*//</
9(9/%*/</
8(8)7(7)6(6)5(5)4(4)3(3)2(2)1(1)0(0//
0/
// 0/ /#/1 bottles/1 bottle/
% 01$Go to the store and buy some more, 099^.

Then, ) corresponds to /&/</ followed by a newline, and ( corresponds to //?/ , which are patterns that will be often used later:
/>/ bottles of beer//^/> on the wall//!/^,//$/>.
//@/$Take one down and pass it around,//#/^.
//*/?1@?0#<0!?0@//%/99!?9@?8#<8!?8@?7#<7!?7@?6#<6!?6@?5#<5!?5@?4#<4!?4@?3#<3!?3@?2#<2!?2@?1#<1!//&/?9#
%*//</
9//?/ 9/%*/</
8//?/ 8/&/</
7//?/ 7/&/</
6//?/ 6/&/</
5//?/ 5/&/</
4//?/ 4/&/</
3//?/ 3/&/</
2//?/ 2/&/</
1//?/ 1/&/</
0//?/ 0//
0/
// 0/ /#/1 bottles/1 bottle/
% 01$Go to the store and buy some more, 099^.

Now, we would decompress some useful strings:

> decompresses to  bottles of beer
^ decompresses to  bottles of beer on the wall
! decompresses to ^,, where ^ is the one above.
$ decompresses to >.\n, where > is the first rule and \n is a newline.
@ decompresses to $ followed by Take one down and pass it around,, where $ is the rule above.

The decompressed code now becomes:
/*/?1 bottles of beer.
Take one down and pass it around,?0 bottles of beer on the wall.
<0 bottles of beer on the wall,?0 bottles of beer.
Take one down and pass it around,//%/99 bottles of beer on the wall,?9 bottles of beer.
Take one down and pass it around,?8 bottles of beer on the wall.
<8 bottles of beer on the wall,?8 bottles of beer.
Take one down and pass it around,?7 bottles of beer on the wall.
<7 bottles of beer on the wall,?7 bottles of beer.
Take one down and pass it around,?6 bottles of beer on the wall.
<6 bottles of beer on the wall,?6 bottles of beer.
Take one down and pass it around,?5 bottles of beer on the wall.
<5 bottles of beer on the wall,?5 bottles of beer.
Take one down and pass it around,?4 bottles of beer on the wall.
<4 bottles of beer on the wall,?4 bottles of beer.
Take one down and pass it around,?3 bottles of beer on the wall.
<3 bottles of beer on the wall,?3 bottles of beer.
Take one down and pass it around,?2 bottles of beer on the wall.
<2 bottles of beer on the wall,?2 bottles of beer.
Take one down and pass it around,?1 bottles of beer on the wall.
<1 bottles of beer on the wall,//&/?9 bottles of beer on the wall.

%*//</
9//?/ 9/%*/</
8//?/ 8/&/</
7//?/ 7/&/</
6//?/ 6/&/</
5//?/ 5/&/</
4//?/ 4/&/</
3//?/ 3/&/</
2//?/ 2/&/</
1//?/ 1/&/</
0//?/ 0//
0/
// 0/ / bottles of beer on the wall.
/1 bottles/1 bottle/
% 01 bottles of beer.
Go to the store and buy some more, 099 bottles of beer on the wall.

Way too easy to strip this down to 329 bytes

Vim, 139 bytes
Saved 6 bytes due to xsot.
i, 99 bottles of beer on the wall.<ESC>YIGo to t<SO> store and buy some more<ESC>qa
3P2xgJX$12.+<CAN>YITake one down a<SO> pass it around<ESC>o<ESC>q98@adk?s
xn.n.ZZ

This is my first attempt at golfing Vim commands, although apparently it's quite popular. I've included the final ZZ in the byte count (write to file and exit) as it seems to be the accepted norm.
Side note: mission accomplished.

Explanation
Command                                    Effect
-------------------------------------------------------------------------------------------
i, 99 bottles of beer on the wall.<ESC>    insert text at cursor
Y                                          copy this line into buffer
IGo to t<SO> store and buy some more<ESC>  insert text at beginning of line
                                           auto-complete "the" (<Ctrl-N>, searches forward)
qa                                         begin recording macro into "a"
<LF>                                       move down one line (if present)
3P                                         paste 3 times before cursor
2x                                         delete 2 characters at cursor
gJ                                         join this line with next (without space between)
X                                          delete character before cursor
$                                          move to last non-whitespace character of line
12.                                        repeat the last edit command (X) 12 times
+                                          move to column 0 of next line
<CAN>                                      numeric decrement (<Ctrl-X>)
Y                                          copy this line into buffer
ITake one down a<SO> pass it around<ESC>   insert text at beginning of line
                                           auto-complete "and" (<Ctrl-N>, searches forward)
o<ESC>                                     insert text on new line
q                                          stop recording macro
98@a                                       repeat macro "a" 98 times
dk                                         delete upwards (this line and the one above it)
?s<LF>                                     move to previous /s/
x                                          delete character at cursor
n.n.                                       repeat last match and delete 2 times
ZZ                                         write to file and exit

Slight improvement: i99 bottles of beer on the wall.<ESC>Yqa3P$r,J5wDr.+<CAN>YITake one down and pass it around, <ESC>o<ESCjq98@aiGo to the store and buy some more, <ESC>kdk?s<LF>xnxnxZZ

@xsot thanks for that. I somehow completely overlooked / and ?.

btw it's possible to reach 153 on anagol with this improvement

@xsot got it ;)

Python 2, 195
i=198
while i:s=`i/2or 99`+' bottle%s of beer'%'s'[1<i<4:];print['%s, '+s+'.','Take one down and pass it around, %s.\n',"Go to the store and buy some more, %s."][i%2+1/i]%(s+' on the wall');i-=1

Took the i/2 idea from Sp3000's answer.

JavaScript ES6, 237 217 208 203 195 193 189 186 bytes
It is getting pretty hard to golf this...
Edit 1: Somebody totally outgolfed me, looks like I have to try harder if I want to have the best Javascript answer.
Edit 2: I honestly can't believe that I managed to golf it that much!
for(i=99,Y=" on the wall",o=k=>k+(i||99)+` bottle${i==1?'':'s'} of beer`;i;)alert(o``+Y+o`, `+o(--i?`.
Take one down and pass it around, `:`.
Go to the store and buy some more, `)+Y+`.`)

Did I mess up somewhere?
I also apologize for using alert, if you want to test my code, replace it with console.log.
Currently, there is one other notable Javascript answer: "99 Bottles of Beer". Check it out! :D

I would re-examine your conditions and you should be able to trim off a couple bytes. Good effort keeping up, though!

@AdamDally Ok!! :D But I don't like copying other people's stuff...(I would feel guilty if I just copied your code...though it would be "acceptable", if I found some improvements) :D

@AdamDally Yay, I messed around with my functions and I saved quite a bunch of bytes! (The crossed out line is getting longer and longer..)

Excellent! Unless there is some alternate path I've completely overlooked I feel like we are both nearing the end.

JavaScript ES6, 210 209 205 199 198 196 bytes
s=""
for(i=299;--i>1;s+=`${i/3|0||99} bottle${5/i^1?"s":""} of beer`+(i%3?" on the wall":i^3?`.
Take one down and pass it around`:`.
Go to the store and buy some more`)+(~i%3?", ":`.

`));alert(s)

This is a crude translation of my C submission. I don't actually know javascript so there is definitely room for improvement.
Edit: Neat, I discovered backticks

you could replace the console.log with alert and you could use ` instead of the ". Then you can replace \n with a newline. (The ticks are used for template strings, those strings can have newlines...)
Also, congrats for beating me!

I think I'll leave the output alone unless someone else beats me as console.log is easier to test. Anyway, thanks for exposing me to backticks!

Wohoo, outgolfed yours! Also, thanks for using || in your code, it sure came in handy...
(Meh, you can change the console.log, and my victory will be reduced to nothing.. XD )

You're proving challenging to keep up with! Alas, it would be interesting to see a breakdown of your code.

@AdamDally Basically, each verse is split into 3 parts with the following structure: %d bottle%s of beer%s%s. The index of each verse (number of bottles) is calculated as i/3|0 while the index of each substring is simply i%3.

Brainfuck, 4028 bytes
This is quite hideous.
Lots of duplication, and very inefficient, so it won't win any prizes. However, I started it, and was determined to finish it.
I may try and improve this, but probably wont, because frankly, my brain is fucked.
Golfed
>>+++[-<+++<+++>>]>>++++++[-<++++++++++<++++++++++>>]<---<--->>>+++++[-<++++++>]
<++>>++++++++++[-<++++++++++>]<-->>++++++++++[-<+++++++++++>]<+>>++++++++++[-<++
++++++++>]<+>>++++++++++[-<++++++++++>]<++>>++++++++++[-<+++++++++++>]<++++++>>+
+++++++++[-<++++++++++>]<++++>>++++++++++[-<+++++++++++>]<-->>++++++++++[-<+++++
++++++>]<++++>>++++++++++[-<+++++++++++>]<+++++>>++++++++++[-<+++++++++++>]>++++
++++++[-<+++++++++++>]<+++++++++>>++++++++++[-<++++++++++>]<--->>+++++[-<++++++>
]<++>>++++[-<++++++++++++>]<---->>++++[-<++++++++++++>]<-->++++++++++>>+++++++++
+[-<+++++++++++>]<--->>++++++++++[-<++++++++++>]>++++++++++[-<+++++++++++>]<++>>
++++++++++[-<++++++++++>]<+++++>>++++++++++[-<+++++++++++>]<+++++++<<<<<<<<<<<<<
<<<<<<<<<<<<[>[->.>.>.>.>.>>>..>>.<<<<.>>>>>>.<<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>.<
<<<<<<<.>>.>>>>>>>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>>>>>>>>.>.<<<<<..>>>>>>>.<.<<<<<<
<<<<<<<<<.>.->.>.>.>>>..>>.<<<<.>>>>>>.<<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>.>>>>>>>.
>.<<<<<<<<<<<.>>>>>>>.>>>>>.<<<<<<<<<<<<<<.<<<.>>.>>>>>>>>.<<<<<<<.<<<.>>>>>>>>>
>>>>>>>>>.<<<<<<<<<<<<<<<<.>>>>>>>>>.<.>>>.<.<<.>>>>>>>>.<<<<<.>>>>>>.<<<<<<<.<<
<..>>>>.>>>>>>>.<<<<<<<<<<<<<<<.>>>>>>>>.<.<<<<.<<<<<<.>>>>>>>>>>>>>>>>>>>.<<<<<
<<<<<<.>>>>>>>>.<<<<.<.<<<<<<<<<<<<<<<.>.>.>.>.>>>..>>.<<<<.>>>>>>.<<<<<<<<<.>>.
>>.<<<<.>.>>..>>>>>.<<<<<<<<.>>.>>>>>>>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>>>>>>>>.>.<<
<<<..>>>>>>>>.>..<<<<<<<<<<<<<<<<<<<]<->+++++++++>.>.>.>.>.>>>..>>.<<<<.>>>>>>.<
<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>.<<<<<<<<.>>.>>>>>>>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>
>>>>>>>.>.<<<<<..>>>>>>>.<.<<<<<<<<<<<<<<<.->.+++++++++>.>.>.>>>..>>.<<<<.>>>>>>
.<<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>.>>>>>>>.>.<<<<<<<<<<<.>>>>>>>.>>>>>.<<<<<<<<<<
<<<<.<<<.>>.>>>>>>>>.<<<<<<<.<<<.>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<.>>>>>>>>>.<
.>>>.<.<<.>>>>>>>>.<<<<<.>>>>>>.<<<<<<<.<<<..>>>>.>>>>>>>.<<<<<<<<<<<<<<<.>>>>>>
>>.<.<<<<.<<<<<<.>>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<.>>>>>>>>.<<<<.<.<<<<<<<<<<<<<<<
.>.>.>.>.>>>..>>.<<<<.>>>>>>.<<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>.<<<<<<<<.>>.>>>>>>
>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>>>>>>>>.>.<<<<<..>>>>>>>>.>..<<<<<<<<<<<<<<<<<<<<]
>--[->>.>.>.>.>>>..>>.<<<<.>>>>>>.<<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>.<<<<<<<<.>>.>
>>>>>>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>>>>>>>>.>.<<<<<..>>>>>>>.<.<<<<<<<<<<<<<<.->.
>.>.>>>..>>.<<<<.>>>>>>.<<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>.>>>>>>>.>.<<<<<<<<<<<.>
>>>>>>.>>>>>.<<<<<<<<<<<<<<.<<<.>>.>>>>>>>>.<<<<<<<.<<<.>>>>>>>>>>>>>>>>>>.<<<<<
<<<<<<<<<<<.>>>>>>>>>.<.>>>.<.<<.>>>>>>>>.<<<<<.>>>>>>.<<<<<<<.<<<..>>>>.>>>>>>>
.<<<<<<<<<<<<<<<.>>>>>>>>.<.<<<<.<<<<<<.>>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<.>>>>>>>>
.<<<<.<.<<<<<<<<<<<<<<.>.>.>.>>>..>>.<<<<.>>>>>>.<<<<<<<<<.>>.>>.<<<<.>.>>..>>>>
>.<<<<<<<<.>>.>>>>>>>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>>>>>>>>.>.<<<<<..>>>>>>>>.>..<
<<<<<<<<<<<<<<<<<<]>>.>.>.>.>>>..>>.<<<<.>>>>>>.<<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>
.<<<<<<<<.>>.>>>>>>>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>>>>>>>>.>.<<<<<..>>>>>>>.<.<<<<
<<<<<<<<<<.->.>.>.>>>..>>.<<<<.>>>>>>.<<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>.>>>>>>>.>
.<<<<<<<<<<<.>>>>>>>.>>>>>.<<<<<<<<<<<<<<.<<<.>>.>>>>>>>>.<<<<<<<.<<<.>>>>>>>>>>
>>>>>>>>.<<<<<<<<<<<<<<<<.>>>>>>>>>.<.>>>.<.<<.>>>>>>>>.<<<<<.>>>>>>.<<<<<<<.<<<
..>>>>.>>>>>>>.<<<<<<<<<<<<<<<.>>>>>>>>.<.<<<<.<<<<<<.>>>>>>>>>>>>>>>>>>>.<<<<<<
<<<<<.>>>>>>>>.<<<<.<.<<<<<<<<<<<<<<.>.>.>.>>>..>>.<<<<.<<<.>>.>>.<<<<.>.>>..>>>
>>.<<<<<<<<.>>.>>>>>>>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>>>>>>>>.>.<<<<<..>>>>>>>>.>..
<<<<<<<<<<<<<<<<<.>.>.>.>>>..>>.<<<<.<<<.>>.>>.<<<<.>.>>..>>>>>.<<<<<<<<.>>.>>>>
>>>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>>>>>>>>.>.<<<<<..>>>>>>>.<.<<<<<<<<<<<<<<.++++++
++>.>.>.>>>..>>.<<<<.<<<.>>.>>.<<<<.>.>>..>>>>>.>>>>>>>.>.>---->>+++++++++>++++<
<<.<<<<<<<<<<<<<<<.<<.>>>>>.<<<.<<.>>>>>.>.<<<.<<<.>>>>>>>>>.<<<<.<<<.>>>>>>.<<<
<<.<<<.>>>>>>>>>>>>.<<.>>>>>>>>.<<<<<.<<<<<<<<<<<<.>>>>>>>>>>>>>>>>>>>>.<<.<<<<<
<.<<<<.<<<<<<<.>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<<.>>>>>>>>>>.>>>>>>>.<<<<<<<<<
<<<<<<<<<.>>>>>>.<<<<<.>>>>>>>>>>>.<.<<<<<<<<<<<<<<..>.>.>.>>>..>>.<<<<.>>>>>>.<
<<<<<<<<.>>.>>.<<<<.>.>>..>>>>>.<<<<<<<<.>>.>>>>>>>>.>>>.<<<<<<<<.>.<<<.<<<.>>>>
>>>>>>>.>.<<<<<..>>>>>>>>.>.

Ungolfed
# SETUP COUNTERS
>>+++[-<+++<+++>>]
>>++++++[-<++++++++++<++++++++++>>]<---<--->>

# SETUP CONSTANTS
>+++++[-<++++++>]<++>
>++++++++++[-<++++++++++>]<-->        # B
>++++++++++[-<+++++++++++>]<+>        # O
>++++++++++[-<++++++++++>]<+>         # E
>++++++++++[-<++++++++++>]<++>        # F
>++++++++++[-<+++++++++++>]<++++++>   # T
>++++++++++[-<++++++++++>]<++++>      # H
>++++++++++[-<+++++++++++>]<-->       # L
>++++++++++[-<+++++++++++>]<++++>     # R
>++++++++++[-<+++++++++++>]<+++++>    # S
>++++++++++[-<+++++++++++>]           # N
>++++++++++[-<+++++++++++>]<+++++++++># W
>++++++++++[-<++++++++++>]<--->       # A
>+++++[-<++++++>]<++>                 # SPACE
>++++[-<++++++++++++>]<---->          # Comma
>++++[-<++++++++++++>]<-->            # Stop
++++++++++>                           # Newline
>++++++++++[-<+++++++++++>]<--->      # K
>++++++++++[-<++++++++++>]            # D
>++++++++++[-<+++++++++++>]<++>       # P
>++++++++++[-<++++++++++>]<+++++>     # I
>++++++++++[-<+++++++++++>]<+++++++   # U

# BACK TO START
<<<<<<<<<<<<<<<<<<<<<<<<<
[>
    [
        -> # Dec x0 counter
        .> # Print 0x char
        .> # Print x0 char
        .>
        .>.>>>..>>.<<<<.>>>>>>. # Bottles
        <<<<<<<<<.
        >>.>>.       # Of
        <<<<.
        >.>>..>>>>>. # Beer
        <<<<<<<<.
        >>.>>>>>>>>. # On
        >>>.
        <<<<<<<<.>.<<<. # The
        <<<.
        >>>>>>>>>>>.>.<<<<<.. # Wall
        >>>>>>>.     # Comma
        <.
        <<<<<<<<<<<<<<<.>.- # Counter with decrement
        >.
        >.>.>>>..>>.<<<<.>>>>>>. # Bottles
        <<<<<<<<<.
        >>.>>.       # Of
        <<<<.
        >.>>..>>>>>. # Beer
        >>>>>>>.     # Stop
        >.           # Newline
        <<<<<<<<<<<.>>>>>>>.>>>>>.<<<<<<<<<<<<<<. # Take
        <<<.
        >>.>>>>>>>>.<<<<<<<. # One
        <<<.
        >>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<.>>>>>>>>>.<. # Down
        >>>.
        <.<<.>>>>>>>>.  # And
        <<<<<.
        >>>>>>.<<<<<<<.<<<..    # Pass
        >>>>.
        >>>>>>>.<<<<<<<<<<<<<<<. # It
        >>>>>>>>.
        <.<<<<.<<<<<<.>>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<.>>>>>>>>. # Around
        <<<<. # Comma
        <.
        <<<<<<<<<<<<<<<.>. # 0x and x0
        >.
        >.>.>>>..>>.<<<<.>>>>>>. # Bottles
        <<<<<<<<<.
        >>.>>.  # Of
        <<<<.
        >.>>..>>>>>. # Beer
        <<<<<<<<.
        >>.>>>>>>>>. # On
        >>>.
        <<<<<<<<.>.<<<. # The
        <<<.
        >>>>>>>>>>>.>.<<<<<.. # Wall
        >>>>>>>>.    # Stop
        >..   # Newline x2
        <<<<<<<<<<<<<<<<<<<      # Reset loop
    ]
    <-
    >+++++++++
    >.>.
    >.
    >.>.>>>..>>.<<<<.>>>>>>. # Bottles
    <<<<<<<<<.
    >>.>>.       # Of
    <<<<.
    >.>>..>>>>>. # Beer
    <<<<<<<<.
    >>.>>>>>>>>. # On
    >>>.
    <<<<<<<<.>.<<<. # The
    <<<.
    >>>>>>>>>>>.>.<<<<<.. # Wall
    >>>>>>>.     # Comma
    <.
    <<<<<<<<<<<<<<<.- # 0x with decrement
    >.+++++++++ # x0 with increment
    >.
    >.>.>>>..>>.<<<<.>>>>>>. # Bottles
    <<<<<<<<<.
    >>.>>.       # Of
    <<<<.
    >.>>..>>>>>. # Beer
    >>>>>>>.     # Stop
    >.           # Newline
    <<<<<<<<<<<.>>>>>>>.>>>>>.<<<<<<<<<<<<<<. # Take
    <<<.
    >>.>>>>>>>>.<<<<<<<. # One
    <<<.
    >>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<.>>>>>>>>>.<. # Down
    >>>.
    <.<<.>>>>>>>>.  # And
    <<<<<.
    >>>>>>.<<<<<<<.<<<..    # Pass
    >>>>.
    >>>>>>>.<<<<<<<<<<<<<<<. # It
    >>>>>>>>.
    <.<<<<.<<<<<<.>>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<.>>>>>>>>. # Around
    <<<<. # Comma
    <.
    <<<<<<<<<<<<<<<.>. # Counter
    >.
    >.>.>>>..>>.<<<<.>>>>>>. # Bottles
    <<<<<<<<<.
    >>.>>.  # Of
    <<<<.
    >.>>..>>>>>. # Beer
    <<<<<<<<.
    >>.>>>>>>>>. # On
    >>>.
    <<<<<<<<.>.<<<. # The
    <<<.
    >>>>>>>>>>>.>.<<<<<.. # Wall
    >>>>>>>>.    # Stop
    >..   # Newline x2
    <<<<<<<<<<<<<<<<<<<<      # Reset outer loop
]
>-- # Decrement counter to only count from 7
# Last 8 loop
[
    -> # Dec counter
    >. # Print x0 char
    >.
    >.>.>>>..>>.<<<<.>>>>>>. # Bottles
    <<<<<<<<<.
    >>.>>.       # Of
    <<<<.
    >.>>..>>>>>. # Beer
    <<<<<<<<.
    >>.>>>>>>>>. # On
    >>>.
    <<<<<<<<.>.<<<. # The
    <<<.
    >>>>>>>>>>>.>.<<<<<.. # Wall
    >>>>>>>.     # Comma
    <.
    <<<<<<<<<<<<<<.- # x with decrement
    >.
    >.>.>>>..>>.<<<<.>>>>>>. # Bottles
    <<<<<<<<<.
    >>.>>.       # Of
    <<<<.
    >.>>..>>>>>. # Beer
    >>>>>>>.     # Stop
    >.           # Newline
    <<<<<<<<<<<.>>>>>>>.>>>>>.<<<<<<<<<<<<<<. # Take
    <<<.
    >>.>>>>>>>>.<<<<<<<. # One
    <<<.
    >>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<.>>>>>>>>>.<. # Down
    >>>.
    <.<<.>>>>>>>>.  # And
    <<<<<.
    >>>>>>.<<<<<<<.<<<..    # Pass
    >>>>.
    >>>>>>>.<<<<<<<<<<<<<<<. # It
    >>>>>>>>.
    <.<<<<.<<<<<<.>>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<.>>>>>>>>. # Around
    <<<<. # Comma
    <.
    <<<<<<<<<<<<<<. # Count
    >.
    >.>.>>>..>>.<<<<.>>>>>>. # Bottles
    <<<<<<<<<.
    >>.>>.  # Of
    <<<<.
    >.>>..>>>>>. # Beer
    <<<<<<<<.
    >>.>>>>>>>>. # On
    >>>.
    <<<<<<<<.>.<<<. # The
    <<<.
    >>>>>>>>>>>.>.<<<<<.. # Wall
    >>>>>>>>.    # Stop
    >..   # Newline x2
    <<<<<<<<<<<<<<<<<<<     # Reset loop
]
# Last but 1 exception
>>. # Counter
>.
>.>.>>>..>>.<<<<.>>>>>>. # Bottles
<<<<<<<<<.
>>.>>.       # Of
<<<<.
>.>>..>>>>>. # Beer
<<<<<<<<.
>>.>>>>>>>>. # On
>>>.
<<<<<<<<.>.<<<. # The
<<<.
>>>>>>>>>>>.>.<<<<<.. # Wall
>>>>>>>.     # Comma
<.
<<<<<<<<<<<<<<.- # x with decrement
>.
>.>.>>>..>>.<<<<.>>>>>>. # Bottles
<<<<<<<<<.
>>.>>.       # Of
<<<<.
>.>>..>>>>>. # Beer
>>>>>>>.     # Stop
>.           # Newline
<<<<<<<<<<<.>>>>>>>.>>>>>.<<<<<<<<<<<<<<. # Take
<<<.
>>.>>>>>>>>.<<<<<<<. # One
<<<.
>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<.>>>>>>>>>.<. # Down
>>>.
<.<<.>>>>>>>>.  # And
<<<<<.
>>>>>>.<<<<<<<.<<<..    # Pass
>>>>.
>>>>>>>.<<<<<<<<<<<<<<<. # It
>>>>>>>>.
<.<<<<.<<<<<<.>>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<.>>>>>>>>. # Around
<<<<. # Comma
<.
<<<<<<<<<<<<<<. # Count
>.
>.>.>>>..>>.<<<<. # Bottle
<<<.
>>.>>.  # Of
<<<<.
>.>>..>>>>>. # Beer
<<<<<<<<.
>>.>>>>>>>>. # On
>>>.
<<<<<<<<.>.<<<. # The
<<<.
>>>>>>>>>>>.>.<<<<<.. # Wall
>>>>>>>>.    # Stop
>..   # Newline x2
# Last 1 exception
<<<<<<<<<<<<<<<<<. # Counter
>.
>.>.>>>..>>.<<<<. # Bottle
<<<.
>>.>>.       # Of
<<<<.
>.>>..>>>>>. # Beer
<<<<<<<<.
>>.>>>>>>>>. # On
>>>.
<<<<<<<<.>.<<<. # The
<<<.
>>>>>>>>>>>.>.<<<<<.. # Wall
>>>>>>>.     # Comma
<.
<<<<<<<<<<<<<<.++++++++ # x with reset to 99
>.
>.>.>>>..>>.<<<<. # Bottle
<<<.
>>.>>.       # Of
<<<<.
>.>>..>>>>>. # Beer
>>>>>>>.     # Stop
>.           # Newline
>----        # Change K to G
>>+++++++++  # Change P to Y
>++++        # Change I to M
<<<.<<<<<<<<<<<<<<<. # Go
<<.
>>>>>.<<<.           # To
<<.
>>>>>.>.<<<.         # The
<<<.
>>>>>>>>>.<<<<.<<<.>>>>>>.<<<<<. # Store
<<<.
>>>>>>>>>>>>.<<.>>>>>>>>.       # And
<<<<<.
<<<<<<<<<<<<.>>>>>>>>>>>>>>>>>>>>.<<. # Buy
<<<<<<.
<<<<.<<<<<<<.>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<<. # Some
>>>>>>>>>>.
>>>>>>>.<<<<<<<<<<<<<<<<<<.>>>>>>.<<<<<. # More
>>>>>>>>>>>.
<.
<<<<<<<<<<<<<<..  # 99
>.
>.>.>>>..>>.<<<<.>>>>>>. # Bottles
<<<<<<<<<.
>>.>>.  # Of
<<<<.
>.>>..>>>>>. # Beer
<<<<<<<<.
>>.>>>>>>>>. # On
>>>.
<<<<<<<<.>.<<<. # The
<<<.
>>>>>>>>>>>.>.<<<<<.. # Wall
>>>>>>>>.    # Stop
>.   # Newline x2

Output:
Try it yourself here!
OK, so it seems the URL is too long to include here, so you will need to copy/paste to try it yourself.
I tested it using this interpreter.

Julia, 227 215 213 bytes
w=" on the wall"
b=" bottles"
o=" of beer"
k=b*o
for n=99:-1:1
println("$n$k$w, $n$k.
$(n>1?"Take one down and pass it around":"Go to the store and buy some more"), $(n>1?"$(n-1)$(k=b*"\b"^(n<3)*o)":"99$b"o)$w.
")end

This uses string interpolation ("$variable") and ternaries to construct the output and print it to STDOUT.
Saved 14 bytes thanks to Glen O!

Rather than (n>1?" bottles":" bottle")" of beer", use " bottle""s"^(n>1)*" of beer" - note that the * is necessary after (n<1) because otherwise it tries to apply (n>1)" of beer" as the power. Also, is there any benefit to using while n>0 over for n=99:-1:1 (which allows the condition at the end to be simpler, and avoids the separate need for n=99)?

Also use actual newlines in place of \n and switch the middle part to interpolation (rather than ",n>1?...",", use $(n>1?...")) to save a character, and move the ,  inside the conditional to before the next part, so it only needs to be written once.

Actually, strike the suggestion of while over for - I just realised that you'd have to subtract one when doing the last number in each verse, so there's no net benefit.

@GlenO Thanks as always for the great suggestions!

Unfortunately, there's an issue with both versions - it says "1 bottles of beer on the wall" at the end of the second last verse.

I figured that, because it's a bit complicated, I'd just edit the fix in, along with a few other tweaks to save some characters. Hope you don't mind.

@GlenO Thanks for the edit. In general we discourage edits that change others' code but I'm sure that your idea is better than what I could have come up with so in this case I do appreciate it.

I was hoping to be able to just suggest a change - it's a bit silly that it doesn't permit that option if you've got enough rep to make an edit without the original answerer's acceptance. I do apologise if I did in any way step on toes for doing it. I just moved the evaluation of the "bottle of beer" part to the end, so it only needs to be done once, and initialised it. I also used a backspace instead of a conditional "s" to save a bit.

@GlenO No worries. Great ideas, as always. :) Thanks!

Windows Batch, 376 bytes
Very very long and ugly:
@echo off
setlocal enabledelayedexpansion
set B=bottles
set C=on the wall
set D=of beer
for /l %%* in (99,-1,1) do (
set A=%%*
if !A! EQU 1 set B=bottle
echo !A! !B! !D! !C!, !A! !B! !D!.
set /a A=!A!-1
if !A! EQU 1 set B=bottle
if !A! EQU 0 (
echo Go to the store and buy some more, 99 bottles !D! !C!.
) else (
echo Take one down and pass it around, !A! !B! !D! !C!.
echo.
))

Labyrinth, 1195 1190 932 bytes
"{"9"
^ }
 ""
 <
 713.101.801..:611.111.89.23}!:({.23.44.001.011.711.111.411.79.23.611.501.23..:511.79.211.23.001.011.79.23.011.911.111.001.23.101.(.:111.23.101.701.79.48\.411..:101.89.23.201.111.23.511.101.801..:611.111.89.23}!:{.23.44..:801.79.911.23.101.401.611.23.(.:111.23.411..:101.89.23.201.111.23.511.101.801..:611.111.89.23}!:{
 _
 3`<
 _ (
""""
"
{(32.111.102.32.98.101:..114.32.111:.(.32.116.104.101.32.119.97.108:..46.\\49.32.98.111.116:..108.101.32.111.102.32.98.101:..114.32.111:.(.32.116.104.101.32.119.97.108:..44.32.49.32.98.111.116:..108.101.32.111.102.32.98.101:..114.46.\71.111.32.116.111.32.116.104.101.32.115:.).111.114.101.32.97.110.100.32.98.117.121.32.115.111.109.101.32.109.111.114.101.44.32.9!9!32.98.111.116:..108.101.115.32.111.102.32.98.101:..114.32.111:.(.32.116.104.101.32.119.97.108:..46.@
 )
 }
 <
 87\\.64..:801.79.911.23.101.401.611.23.(.:111.23.411..:101.89.23.201.111.23.511
 _
 3`<
 _ (
v"""

This is a bit excessive...
While Labyrinth isn't particularly good at printing strings (because you need to push all the character codes), I think it should be possible to do better than this by using more but shorter long lines and getting even crazier with the grid rotation.
As I think any golfing improvements will substantially change the structure of this code, I'll wait with an explanation until I'm out of ideas how to golf it further.

GolfScript, 143 bytes
[99.{[', '\.' bottle''s of beer'@(:i!>' on the wall''.
'n].1>~;5$4<\'Take one down and pass it around'i}**'Go to the store and buy some more'](

May still be room for improvement. Getting close to the final revision, I think.

Just improved the anagol GolfScript record by 12 bytes.

Python, 254 bytes
b,o,s,t="bottles of beer","on the wall","bottle of beer",".\nTake one down and pass it around,"
i=99;exec'print i,b,o+",",i,b+t,i-1,b,o+".\\n";i-=1;'*97
print"2",b,o+", 2",b+t+" 1",s,o+".\n\n1",s,o+", 1",s+".\nGo to the store, buy some more, 99",b,o+"."

Pretty straightforward, assign some of the most common phrases, print each bit from 99 to 3, then print the last lines by adding together the variables and some strings.

Mathematica, 238 226 222 224 bytes
Saved several bytes thanks to Martin Büttner.
a={ToString@#," bottle",If[#<2,"","s"]," of beer"}&;b=a@#<>" on the wall"&;Echo[{b@n,", ",a@n,".
"}<>If[n<2,"Go to the store and buy some more, "<>b@99<>".","Take one down and pass it around, "<>b[n-1]<>".
"]]~Do~{n,99,1,-1}

C, 303 299 297 bytes
#define B"%d bottle%s of beer"
#define O" on the wall"
#define P printf(
#define V(n,m,S)q(n);P O);P", ");q(n);P S);q(m);P".\n");
*s[]={"","s"};q(n){P B,n,s[n>1]);}main(){for(int i=99;--i;){V(i+1,i,".\nTake one down and pass it around, ")P"\n");}V(1,99,".\nGo to the store and buy some more, ");}

Compile with gcc -std=c99 -w.

Golf down to 300 bytes: ...for(int i=99;--i;){V(i+1,i,"....

@GlennRanders-Pehrson Thanks. I also removed the linebreak before main().

JavaScript ES6, 214 bytes

Edit: Deleted all previous code, view edits if you want to see the older code.

Limited popups:
p='.';o=" of beer";e=o+" on the wall";i=99;u=m=>i+" bottle"+(i==1?'':'s');while(i>0){alert(u()+e+", "+u()+o+p+(--i>0?"Take one down and pass it around, "+u()+e:"Go to the store and buy some more, 99 bottles"+e)+p)}

Expanded:
p='.';
o=" of beer";
e=o+" on the wall";
i=99;
u=m=>i+" bottle"+(i==1?'':'s');
while(i>0){
    alert(u()+e+", "+u()+o+p+(--i>0?"Take one down and pass it around, "+u()+e:"Go to the store and buy some more, 99 bottles"+e)+p)
}

@commenters: Thanks for the idea of arrow functions, saved 15 bytes
For infinite beer just use this code here,  212 bytes
p='.';o=" of beer";e=o+" on the wall";i=99;u=m=>i+" bottle"+(i==1?'':'s');while(i>0){alert(u()+e+", "+u()+o+p+(--i>0?"Take one down and pass it around, "+u()+e:"Go to the store and buy some more, "+u(i=99)+e)+p)}

I can't get the code to run, what browser/environment are you using? "SyntaxError: missing ; before statement" Also, have a look at arrow functions!

Took me a moment to find the syntax error +o' as it were then changed to +o+'. Also saw opportunity for changing the loop and answer. Will be checking out arrow functions now :)

232 bytes for the last solution: b=a=>a+" bottle"+(1--i;)l="\n",p="."+l,o=" of beer",e=o+" on the wall",alert(l+b(i)+e+", "+b(i)+o+p+(11];print n%2*"GToa kteo  otnhee  dsotwonr ea nadn dp absusy  isto maer omuonrde,,  "[n>1::2]+`n/2or 99`,s,"on the wall"+[", %d %s."%(n/2,s),".\n"[:n]][n%2];n-=1

The spec is quite underspecified in terms of whitespace, so here I'm assuming that the last line needs to have a single trailing newline. If the spec clarifies otherwise I'll update this answer.
I'm pretty happy with this, but looking at anarchy golf I feel like this can be golfed still, possibly with a different approach.

CJam, 149 148 146 144 138 137 134 bytes
00000000: 39 39 7b 5b 22 2c 2e 22 22 01 bd 8f 2d b4 49 b5 f5  99{[",.""...-.I..
00000011: 9d bd 21 e8 f2 72 27 df 4d 4f 22 7b 32 36 39 62 32  ..!..r'.MO"{269b2
00000022: 35 62 27 61 66 2b 27 6a 53 65 72 28 65 75 5c 2b 2a  5b'af+'jSer(eu\+*
00000033: 7d 3a 44 7e 4e 4e 32 24 32 3e 29 34 24 4a 3c 5c 4e  }:D~NN2$2>)4$J<\N
00000044: 5d 73 27 78 2f 39 39 40 2d 73 2a 7d 2f 27 73 2d 5d  ]s'x/99@-s*}/'s-]
00000055: 22 07 9c 4b a2 4e 15 d7 df d5 82 88 c9 d9 a7 ad 37  "..K.N..........7
00000066: 16 7e 76 22 44 33 35 2f 28 5d 22 41 90 1d b1 f3 69  .~v"D35/(]"A....i
00000077: ba 3d 05 45 81 50 af 07 e4 1b 38 f7 19 22 44        .=.E.P....8.."D

The above hexdump can be reversed with xxd -r. Try it online in the CJam interpreter.
At the cost of 9 extra bytes, for a total of 143 bytes, we can avoid unprintable characters:
99{[", X bottles of beer on the wall."NN2$2>)4$J<\N]s'X/99@-s*}/'s-]"Take one down and pass it around"*35/("Go to the store and buy some more"\

How it works
99{         e# For each I in [0 ... 98]:
  [         e#
    ",."    e#     Push that string.
    "…"     e#     Push a string.
    {       e#     Define a decoder function:
      269b  e#       Convert the string from base 269 to integer.
      25b   e#       Convert from integer to base 25.
      'af+  e#       Add 'a' to each base-25 digit.
      'jSer e#       Replace j's with spaces.
      (     e#       Shift the first character from the resulting string.
      eu    e#       Convert it to uppercase.
      \+    e#       Prepend it to the remaining string.
      *     e#       Join the string/array on the stack, using the
            e#       generated string as delimiter.
    }:D~    e#     Name the function D and execute it.
            e#     This pushes ", x bottles of beer on the wall.".
    NN      e#     Push two linefeeds.
    2$      e#     Push a copy of the generated string.
    2>)     e#     Discard the first two characters and pop the dot.
    4$      e#     Push another copy of the generated string.
    J<      e#     Discard all but the first 19 characters.
            e#     This pushes ", x bottles of beer on the wall".
    \N      e#     Swap the string with the dot and push a linefeed.
  ]s        e#   Collect in an array and cast to string.
  'x/       e#   Split at occurrences of 'x'.
  99@-      e#   Rotate I on top of 99 and compute their difference.
  s*        e#   Cast to string and and join.
            e#   This replaces x's with 99-I.
}/          e#
's-         e# Remove all occurrences of 's' for the last generated string.
]           e# Wrap the entire stack in an array.
"…"D        e# Join the array with separator "Take one down and pass it around".
35/(        e# Split into chunks of length 35 and shift out the first.
            e# This shifts out ", 99 bottles of beer on the wall.\n\n".
]           e# Wrap the modified array and shifted out chunk in an array.
"…"D        e# Join the array with separator "Go to the store and buy some more".

Base 259? Interesting.

Base 256 had carriage returns, which do not work with the online interpreter. Luckily, base 259 produces no characters outside the ISO 8559-1 range.

I assumed CJam would be at least 2 bytes shorter than GolfScript - almost trivially. Wasn't expecting 137 though!

@primo To be fair, most of the savings are from base conversion. Sadly, using base twice already adds 8 bytes in GolfScript...

PHP, 172 bytes
<?for(;100>$a=1+$a." bottle$o[21] of beer";$o="
Take one down and pass it around, $b.

".$g="$b, $a.$o")$b="$a on the wall";echo"$g
Go to the store and buy some more, $b.";

The most significant trick is $o[21]. On the first iteration, $o will be undefined, resulting in an empty string. On each iteration after, it refers to the first s of pass.

Windows produces 99 bottlea of beer on the wall. You could go safe with 22 instead of 21.
And You don´t need the <? if You use -r.

C, 230 bytes
My first codegolfing and learning C!
#define B "%d bottle%s of beer"
main(i,a){for(i=99;i;i--)a=i<2,printf(B" on the wall, "B".\n%s, "B" on the wall.\n\n",i,"s"+a,i,"s"+a,a?"Go to the store and buy some more":"Take one down and pass it around",a?99:i-1,"s"+(i==2));}

Compiled with gcc -std=c99 -w beer.c
Readable (sort of):
#define B "%d bottle%s of beer"

main(i,a){
  for (i = 99; i; i--) {
    a=i<2;
    printf(B" on the wall, "B".\n%s, "B" on the wall.\n\n",
      i, "s"+a, i, "s"+a,
      a? "Go to the store and buy some more":"Take one down and pass it around",
      a? 99 : i-1, "s"+(i==2));
  }
}

My first entry (293 bytes):
#define BOT "bottle%s of beer"
main(){
char *e[]={"Go to the store an buy some more","Take one down and pass it around"};char *z[]={"","s"};
for(int i=99;i>0;i--){int j=i>1?i-1:99;char* s=z[i>1];
printf("%d "BOT" on the wall, %d "BOT". %s, %d "BOT" on the wall.\n",i,s,i,s,e[i>1],j,z[j>1]);}}

Haha, I love all these nasty tricks i applied that i never though could even exist in C!

Typo: Go to the store an buy

@feersum Thanks, fixed.

Welcome to PPCG!  Lots more opportunity for more golfing this answer.  Check out these tips

You're missing the newline after "beer."

@DigitalTrauma Thank you! wow great link, really usefull! chopped like 15 bytes with some of those tricks!

@Ray Yes, thanks, fixing format of the output...

Oracle SQL, 393 326 320 306 324 bytes*l
Grew a few bytes to fix error noted by C.L.
select b||decode(l,0,c||d||', '||b||c||'. Go to the store and buy some more, 99 bottle','s'||c||d||', '||b||'s'||c||'. Take one down and pass it around, '||l||' bottle')||decode(l,1,'','s')||c||d||'.'
from(select level-1 l,level||' bottle' b,' of beer' c,' on the wall' d from dual connect by level <= 99)order by l desc;

Second-to-last line ends with "1 bottles of beer".

Fixed - thanks for the catch on that.

if you want to be on the leaderboard, please make your header be a markdown heading #Language Name

PHP, 250 246 244 233 228 bytes
I believe this is smallest so far. This is based on the 247 shot, with some modifications to further minimise.
Minimised
<?php $b=99;function x($n){return"$n bottle".($n-1?'s':'')." of beer";}$y=" on the wall";while($b){$c=x($b);echo"$c$y, $c.\n",--$b?"Take one down and pass it around":"Go to the store and buy some more",", ".x($b?:99)."$y.\n\n";}

Expanded
<?php

$b=99;
function x($n){return"$n bottle".($n-1?'s':'')." of beer";}
$y=" on the wall";
while(b){
    $c=x($b);
    echo"$c$y, $c.\n",--$b?"Take one down and pass it around":"Go to the store and buy some more",", ".x($b?:99)."$y.\n\n";
}

As you can run PHP from command line with php -r which is equivalent of perl -e and similar, is acceptable to omit the opening tag. See the related discussion in Running PHP with -r instead of code tags.

Ah, thanks @manatwork, I guess I could then get mine down a bit more.

while($b) works just as well.

There is an extra newline at the end though.

Thanks for the whole suggestion! The newline is required though, to have a line in between verses

You can get this down to 216 bytes: function x($n){return"$n bottle".($n-1?s:'')." of beer";}$y=" on the wall";for($b=99;$b;){$c=x($b);echo"$c$y, $c.↵",--$b?"Take one down and pass it around":"Go to the store and buy some more",", ".x($b?:99)."$y.↵↵";}. Replace ↵ with an actual newline.

We now have arrow functions :-) Try this with only 215 bytes INCLUDING THE <?php: https://tio.run/##HY5BDoIwEEX3nGIymQRJsAt3UCtLL@AFWhiCETsNrVFiPDs2LP/Lz38/TGHbzl2YAtDHjP5AvjIXJA9OUpoZVSbnU1eWbRnLSiHICI55QU2ryclDmhjedp5Rj7IcyJmm0eR09aXe0CeDSnM/CVJPaw3UqwLr45Fchzf74LzAMMjbg/UDBBsj3BPYRV5@wBavAkl2RUyy8F5yrxWiPBmemWCNNaDaRV3bNPkiraooUP@27Q8

Beam, 1141 1109 bytes
I still have a lot of room to golf this further with all the empty spaces, but it is getting really hard to follow and breaks quite easily :)
It's very similar to the one that I posted for this question, except it goes to the store before the beer reaches 1 and the cells used for the parameters have been shifted.  I've also change the layout considerably.  I'll try and do an explanation once I try out a couple more layouts.
P'P''''>`++++++)++'''P>`+++++++++++++)'''''''''''P+++++++++++++`P```>`++\ v@@++++++++++L(`<@+p'''''''''PL<
v``P''(++++++`<P'''''''''(++++++++`<L```P'+++++P'+++P'++++++P'++++P''''(/> p++@---@``p@'''''p+++@`> `)''' 'p-@''p\
>''p:'p@'p@'\>n'   >`>`)'''p@''''p@\>n'''p@''''p@-@````p@'''''p@`>`)'''''/v  `P+p``@p'''(`<@-p''''''''P'+up(`<`@@/
^/``@@p'@p''/ >'''\ /-p'@p'@p``@p``/`>-'P''''''''p+@>`)''p`n`L++++++++++@S 'p@````p@````p@'''''p@`p@````p@'''''p@```p++@---@``p@'''''p-@+@`p@+++@``p-@``p@'p-@'''p-@```p++@`p@'p@''''p+@++++@`````p@'''''p-@`p@--@``p-@``p@''''p--@p@+++@``p-@''''p-@>`)'''p@'p+:`p@'p@'''p@'p@@``p@`p-@'''p-@`>`)'''p@''''p@``p@``p@'p@'p-@@'''p--@`>`)'''p@''''p@-@````p@'''''p@`>`)'''''p++@---@``p@'''''p+++@`>`)''''p-@''p@@'''p+@H
^\p@`p-@```p`//'''/ \@@'''p--@`>`)'p/``````@pS@++++++++++L`<vP+p`P-p`P-p`@ p'''(`<@-p''''@--p``@-p`@+p'@p`@--p''''@-p'@p`````@p'''@+++p''@p```\
^             \'p-@/v               \ p-@''p-@`p-@``p@''''p@ -@``p-@``p@'p ++@'''p@'p+++@`p-@````p@'p-@'''p-@```p++@`p@''''p+@```p-@''''p-@@``/
^                   <                                       <             <

Try it in the stack snippet here

PARI/GP, 181 bytes
for(i=1,99,{G=Str("Take one down and pass it around, "b=Str(a=Str(i" bottl"e" of beer")" on the wall")".

"g=Str(b", "a".
"G));e=es);print(g"o to the store and buy some more, "b".")

Using the variable name G conveniently saves a byte. The closing brace doesn't seem to be required to end a multi-line context, strangely.
The shortest solution I know of previous to this is 32 bytes heavier.

Japt, 157 127 bytes
1oL w £A=X+` Þ¤{X-1?'s:P}  Þ8`;B=A+`  e Ø!` +".

{B}, {A}.
{´X?`Take e Üµ d ps  ÂÐ`:`Go  e ÐJe d ¿y Ñ  Ú`}, "Ã¬éHn

Markdown hates unprintables (unlike the shoco string compression library), so here's a hexdump:
00000000: 316f 4c20 7720 a341 3d58 2b60 20de ada4 7b58    1oL w £A=X+` Þ.¤{X
00000012: 2d31 3f27 733a 507d 208f 20de 3860 203d 412b    -1?'s:P} . Þ8` =A+
00000024: 6020 8d20 9065 20d8 2160 3b42 2b22 2e0a 0a7b    ` . .e Ø!`;B+"...{
00000036: 427d 2c20 7b41 7d2e 0a7b b458 3f60 5461 6b65    B}, {A}..{´X?`Take
00000048: 208d 6520 dcb5 2084 6420 7086 7320 8a20 c2d0     .e Üµ .d p.s . ÂÐ
0000005A: 9860 3a60 476f 2091 2090 6520 d04a 6520 8464    .`:`Go . .e ÐJe .d
0000006C: 20bf 7920 d120 20da 9060 7d2c 2022 c3ac e948     ¿y Ñ  Ú.`}, "Ã¬éH
0000007E: 6e                                              n

Try it online!
This could probably be made shorter by saving the repeated sections to variables. Absolutely correct, past me. Japt is now the shortest language without a built-in!

Seems to be missing the empty line between verses.

@primo Thanks, fixed at the cost of one byte.

x86 MS-DOS .COM file, 176 bytes
Advantages:

bitwise manipulation allows efficient encoding (However, in the end I settled on a relatively unpacked encoding which is pretty readable, just using the 8th bit of each byte to store spaces.  I tried denser encodings that got the data to under 70 bytes, but the program size went up just as much.)
.COM files are widely supported from MS-DOS 1 through the present (I'm running in dosemu, sticking with 8086 commands only)

Disadvantages:

conditional jumps can get real obfuscated real quickly
some simple tasks are hard; it takes ~20 bytes to print the number of bottles

Hex dump (pass this into xxd -r to regenerate the 176-byte file):
0100: be a8 01 bd 63 00 40 75 06 4d 74 f7 be a4 01 8a  ....c.@u.Mt.....
0110: 0c 46 8a 1c 46 80 fb 89 75 18 89 e8 b7 0a f6 f7  .F..F...u.......
0120: b7 01 05 30 30 3c 30 92 b4 02 74 02 cd 21 88 f2  ...00<0...t..!..
0130: cd 21 38 2f 79 04 b2 20 cd 21 b2 7f 22 17 4d 75  .!8/y.. .!..".Mu
0140: 05 80 fb 8f 74 02 cd 21 45 43 e2 c9 81 fe ae 01  ....t..!EC......
0150: 76 b5 c3 54 61 6b 65 ef 6e 65 e4 6f 77 6e e1 6e  v..Take.ne.own.n
0160: 64 f0 61 73 73 e9 74 e1 72 6f 75 6e 64 47 6f f4  d.ass.t.roundGo.
0170: 6f f4 68 65 f3 74 6f 72 65 e1 6e 64 e2 75 79 f3  o.he.tore.nd.uy.
0180: 6f 6d 65 ed 6f 72 65 2c 20 e2 6f 74 74 6c 65 73  ome.ore, .ottles
0190: ef 66 e2 65 65 72 ef 6e f4 68 65 f7 61 6c 6c 2e  .f.eer.n.he.all.
01a0: 0d 0a 0d 0a 1a 53 1d 87 16 89 0f 87 03 9f 33 6d  .....S........3m

Explanations:
Bytes 0100 through 0152 (83 bytes) are commands, unassembled using debug:

0100 BEA801   MOV SI,01A8     ; point to 3rd substring
0103 BD6300   MOV BP,0063     ; begin with 99 bottles
0106 40       INC AX          ; only used to unset zero flag after last bottle taken
0107 7506     JNZ 010F        ; {when referred from 0150...if finished 5th substring:
0109 4D       DEC BP          ;    take down a bottle
010A 74F7     JZ 0103         ;    took last bottle, reset 99 bottles (and proceed to 6th)
010C BEA401   MOV SI,01A4     ;    or if not last bottle, point to 1st substring}
010F 8A0C     MOV CL,[SI]     ; load counter for substring
0111 46       INC SI
0112 8A1C     MOV BL,[SI]     ; load start pointer for substring
0114 46       INC SI
0115 80FB89   CMP BL,89       ; {  {check location to print # of bottles,
0118 7518     JNZ 0132        ;        if so...
011A 89E8     MOV AX,BP
011C B70A     MOV BH,0A
011E F6F7     DIV BH          ;        divide by 10
0120 B701     MOV BH,01       ;  (leave BH=1 for rest of program so [BX] points correctly)
0122 053030   ADD AX,3030     ;        convert to ascii numerals
0125 3C30     CMP AL,30       ;        {if tens digit > 0,
0127 92       XCHG AX,DX
0128 B402     MOV AH,02       ;  (leave AH=2 for rest of program to print to stdout)
012A 7402     JZ 012E
012C CD21     INT 21          ;           print tens digit}
012E 88F2     MOV DL,DH
0130 CD21     INT 21          ;        print ones digit}
0132 382F     CMP [BX],CH     ;    {check if character's 8th bit is set
0134 7904     JNS 013A        ;       if so...
0136 B220     MOV DL,20
0138 CD21     INT 21          ;       print " "}
013A B27F     MOV DL,7F
013C 2217     AND DL,[BX]     ;    load character without 8th bit
013E 4D       DEC BP          ;    {temporarily check if --bottle count = 0
013F 7505     JNZ 0146
0141 80FB8F   CMP BL,8F       ;       if so, check if at location of "s" to skip
0144 7402     JZ 0148
0146 CD21     INT 21          ;       print (unless skipping "s")
0148 45       INC BP          ;       undo temporary check}
0149 43       INC BX
014A E2C9     LOOP 0115       ;    proceed to next encoded character of substring}

014C 81FEAE01 CMP SI,01AE     ; check if we just finished the 5th of the 6 substrings
0150 76B5     JBE 0107        ; if the 5th or less, triage further at line 0107
0152 C3       RET             ; end after finishing 6th substring

Bytes 0153 through 01a3 (81 bytes) are the pretty readable encoded text

Bytes 01a4 through 01af (12 bytes) are (length,start) pairs pointing to substrings
    "Take...around"
    ", #bottles...wall.CRLFCRLF"
    "#bottles...wall"
    ", #bottles...beer"
    ".CRLF"
    "Go...wall.CRLF"

T-SQL,  671 653 517 Bytes
Saw an Oracle one so I thought of doing it but I went for a cursor... Not competition level but still a lot of fun =)
DECLARE @q CHAR(15) = 'bottles of beer',@w CHAR(11)='on the wall',@e CHAR(33)='Take one down and pass it around,',@r INT=99,@i INT=1,@ INT=99 WHILE(@>=1)BEGIN SET @i=@-1 IF(@>1)BEGIN PRINT CONVERT(VARCHAR(2),@)+' '+@q+' '+@w+', '+CONVERT(VARCHAR(2),@)+' '+@q+'.'+CHAR(10)+@e+' '+CONVERT(VARCHAR,@i)+' '+@q+' '+@w+'.'+CHAR(13)END ELSE BEGIN PRINT CONVERT(VARCHAR(1),@)+' bottle of beer '+@w+', '+CONVERT(VARCHAR(1),@)+' bottle of beer.'+CHAR(10)+'Go to the store and buy some more, 99 '+@q+' '+@w+'.'END SET @=@-1 END

Proper indent:
DECLARE @q CHAR(15) = 'bottles of beer',
 @w CHAR(11) = 'on the wall',
 @e CHAR(33) = 'Take one down and pass it around,',
 @r INT = 99,
 @i INT = 1,
 @ INT = 99
WHILE (@ >= 1)
    BEGIN
    SET @i = @ -1
    IF (@ > 1)
        BEGIN
        PRINT
            CONVERT(VARCHAR(2), @)+' '+@q+' '+@w+', '+CONVERT(VARCHAR(2), @)+' '+@q+'.'+CHAR(10)+
            @e+' '+CONVERT(VARCHAR, @i)+' '+@q+' '+@w+'.'+CHAR(13)
        END
    ELSE
        BEGIN
        PRINT
            CONVERT(VARCHAR(1), @)+' bottle of beer '+@w+', '+CONVERT(VARCHAR(1), @)+' bottle of beer.'+CHAR(10)+
            'Go to the store and buy some more, 99 '+@q+' '+@w+'.'
        END
    SET @ = @-1
    END

Nice work. Quite a few places to save bytes: #1 Use CONCAT() instead of +, it implicitly casts the integers and you don't need CONVERT. #2: Eliminate @r (unused) and @i (just use @-1), and remove spaces around =. #3: String literals can include returns, use that instead of '+CHAR(10)+'. #4: This is more work but consider a label and a GOTO instead of a WHILE loop. #5: Include spaces at the beginning of @q and @w, and a (period return) at the beginning of @e.

Today I learned: @ is a valid variable name in T-SQL. Have a +1.

@primo :) also: @@, @@@, @@@@...

R, 267 bytes
Not the shortest solution out there for R but it uses a different approach (namely dataframes).
d=data.frame(i=1:99,j=c(99,1:98));d$b=" bottles of beer";d$t="Take one down and pass it around";d[1,3:4]=c(" bottle of beer","Go to the store and buy some more");d$B=d$b[d$j];w=" on the wall";cat(rev(with(d,sprintf("\n%i%s%s, %i%s.\n%s, %i%s%s.",i,b,w,i,b,t,j,B,w))))

Indented, with newlines:
d = data.frame(i=1:99,j=c(99,1:98))
d$b = " bottles of beer"
d$t = "Take one down and pass it around"
d[1,3:4] = c(" bottle of beer","Go to the store and buy some more")
d$B = d$b[d$j]
w = " on the wall"
cat(rev(with(d,sprintf("\n%i%s%s, %i%s.\n%s, %i%s%s.",i,b,w,i,b,t,j,B,w))))

Here it is on ideone.

C Preprocessor, 753 bytes
#ifndef T
#define N
#define T 9
#define G(A,B) I(A,B)
#define I(A,B) A##B
#define X Take one down and pass it around,
#define Y G(bottle,s) of beer
#define Z Y on the wall
#define E(x) G(T,x) Z, G(T,x) Y.
#define F(x) X G(T,x) Z.
#endif
E(9)
F(8)
N
E(8)
F(7)
N
E(7)
F(6)
N
E(6)
F(5)
N
E(5)
F(4)
N
E(4)
F(3)
N
E(3)
F(2)
N
E(2)
#if T+0
F(1)
N
E(1)
F(0)
N
E(0)
#if T<2
#undef T
#define T
#elif T<3
#undef T
#define T 1
#elif T<4
#undef T
#define T 2
#elif T<5
#undef T
#define T 3
#elif T<6
#undef T
#define T 4
#elif T<7
#undef T
#define T 5
#elif T<8
#undef T
#define T 6
#elif T<9
#undef T
#define T 7
#else
#undef T
#define T 8
#endif
F(9)
N
#include __FILE__
#else
#define s
X 1 Z.
N
1 Z, 1 Y.
#undef s
Go to the store and buy some more, 99 Z.
#endif

Try it online!
It is my privilege to submit this C preprocessor solution to this catalog.  Note that this is a pure C Preprocessor solution (or, as I prefer to call it, a C Preprocessor solution).  To my knowledge this is the first such solution to be posted to a catalog (there are a few claimed "C preprocessor" solutions around, but they're really things like C programs using the C preprocessor to generate strings).
5 bytes could be saved if I exploited the file name (e.g. changing #include __FILE__ to #include "f").
If anyone can help golf this further let me know.

Lua 5.1, 211 207 bytes
(Also works in Lua 5.2 and Lua 5.3.)
4 bytes saved thanks to primo.
B=" on the wall"P=""S="Go to the store and buy some more"for i=1,99 do
A=i.." bottle"..P.." of beer"S=([[Take one down and pass it around, AB.

AB, A.
S]]):gsub(".",_G)P="s"end
print(S:sub(68)..S:sub(33,65))

I chop up the song into chunks that have the same number of bottles of beer which simplifies the interpolation at the cost of some slicing at the end. This also allows constructing the verses backwards which simplifies the logic for pluralizing "bottle".
:gsub(".",_G) is a fun little call that replaces each character in a string with its value as a global variable (with no replacement if the global variable is not defined).
I played around with some of the tricks seen in the Python answers, but I wasn't able to beat this yet. It may still be possible to do a better in-order solution though.
Try it online

You can initialize S to S='Go to the store and buy some more'. Otherwise nice solution. The gsub on _G is brilliant.

Python, 245 223 bytes
w="bottles of beer on the wall"
[print(i,w+',',w[:15]+'.\nTake one down and pass it around,',str(i-1),w+'.\n')for i in range(99,1,-1)]
print('1 bottle',w[8:],',1 bottle',w[8:15]+'.\nGo to the store and buy some more,',99,w)

edit: thank you GamrCorps for the suggestion to cut down over 20 bytes!

Welcome to PPCG.SE! Nice first post! There are some simple golfing improvements such as single-character variables and the starting index for the slicing (wall[0:8] can be wall[:8]). Otherwise, great job, I hope to see you around!

Bash, 167 bytes
Saved 2 bytes due to Sisyphus.
eval a={1..99}'" bottle$s of beer"
b="$a on the wall";f=$b,\ $a.$o;o="
Take one down and pass it around, $b.

$f";s=s';echo "$f
Go to the store and buy some more, $b."

Try it online!

'a="' can become a=\" for a byte saved.

Actually, even better you can just replace the whole construction with eval a={1..99}'" bottle$s of beer"

PHP, 274 269 bytes
<?php $a=' bottles';for($i=99;$i>0;$i--){$d="Take one down and pass it around, ".($i-1)."$a

";if($i==1){$a=rtrim($a,'s');$d="Go to the store and buy some more, 99 bottles";}$d.=" of beer on the wall.";$s.="$i$a of beer on the wall, $i$a of beer.
".$d;}echo$s;

Depending on you few output,
 could be replaced by \n, and you can replace rtrim() with trim() as there is no s in front of 'bottles'

Pyth, 157 156 150 149 148 145 bytes
L+b%" bottle%s of beer on the wall."<\stbVK99s[PyKJ", "<13yK\.)++?=tK"Take one down and pass it around""Go to the store and buy some more"Jy|K99k

Try it online.

You can shorten this a bunch using the new ." operator. I can't seem to get the online compiler to make a friendly link to a working one, but here is a paste of the hexdump. I'm not sure if it is a bug, but I couldn't get ." to work with a leading space, but I believe it still saves one byte.

Perl 5, 243 bytes
A shorter Perl 5 solution is posted as a community wiki solution here.
$}=" bottle";$s='s';$o=" of beer";$w=" on the wall";$i=99;while($i>1){say"$i$}$s$o$w, $i$}$s$o.";$s=''if--$i<2;say"Take one down and pass it around, $i$}$s$o$w.\n"}say"$i$}$s$o$w, $i$}$s$o.";say"Go to the store and buy some more, 99$}s$s$o$w."

Using the free -M5.010 switch for "say" instead of "print".
Cheers.

I think it'd be cheaper to use last if$i<2; before the $s assignment (and change the while to while($i)) than to have the extra say in the second-to-last line.

(Oh, and there's a shorter version at http://www.99-bottles-of-beer.net/language-perl-727.html. :-))

About that link. It doesn't go to the store, as the challenge requests. But thanks for the hint, the different methods used are interesting.

Perl 5, 159
@c=(@b=(++$n,bottle.'s'x@-,of,beer),on,the,wall),s//Take one down and pass it around, @c.

@c, @b.
/,until/99\D+/;say$'."Go to the store and buy some more, $&"

Based on the solution from eyepopslikeamosquito found here.
Requires the free -M5.010 switch to use "say".

PHP, 251 bytes
This accounts for the plural/singular problem.
<?php for($i=99;$i>0;$i--){$b=" of beer";$s=" bottles$b";$r=" bottle$b";$w=" on the wall";$h=$i-1;echo$h>=1?"$i$s$w, $i$s.\nTake one down and pass it around, $h".($h<2?$r:$s)."$w.\n\n":"$i$r$w, $i$r. \nGo to the store and buy some more, 99$s$w.\n\n";}

Readable:
for ($i=99; $i > 0; $i--) {
  $b = " of beer";
  $s = " bottles$b";
  $r = " bottle$b";
  $w = " on the wall";
  $h = $i - 1;
  echo $h >= 1 ? "$i$s$w, $i$s.\nTake one down and pass it around, $h" . ($h<2 ? $r : $s) . "$w.\n\n" : "$i$r$w, $i$r. \nGo to the store and buy some more, 99$s$w. \n\n";
}

Try it online

230 bytes: http://pastebin.com/QUc2hp6X, try on http://sandbox.onlinephpfunctions.com/code/4baab691146632ae000a5e91c1b7c9ff5539ea9d

C#, 274 bytes
Direct copy from my answer on another question (it was popularity, but I just treated it as golf). Never did get around to golfing it further.
class
P{static
void Main(){
for(int i=99;i>0;
)System.Console.Write
(i+"{1}{2}{3}, {0}{1}{2} of beer.\n"+(i>1?
"Take one down and pass it around, {4}{1}{5}{3}.\n\n":
"Go to the store and buy some more, 99{1}s{3}."
),i--," bottle",i>0?"s":"",
" of beer on the wall"
,i,i>1?"s":""
);}}

Nice one! Why you indent like this instead of one line?

Nevermind I guess it is just for reading.

@aloisdg I think I made a weak attempt to shape the code in some fun way (like an arrow), but failed.

Java, 290 bytes
class C{public static void main(String[]p){String b=" bottle",s=b+"s",o=" of beer",w=o+" on the wall",c=", ",n=".\n",u;for(int i=99;i>0;){u=i>2?s:b;System.out.println(i+s+w+c+i--+s+o+n+(i>0?"Take one down and pass it around, "+i+u:"Go to the store and buy some more, 99"+b+"s")+w+n);s=u;}}}

Ungolfed:
class C {
    public static void main(String[] p) {
        String b = " bottle", s = b + "s", o = " of beer", w = o + " on the wall", c = ", ", n = ".\n", u;
        for (int i = 99; i > 0;) {
           u = i > 2 ? s : b;
           System.out.println(i + s
             + w
             + c
             + i--
             + s
             + o
             + n
             + (i > 0 ? "Take one down and pass it around, " + i + u
                 : "Go to the store and buy some more, 99" + b + "s") + w + n);
           s = u;
        }
    }
}

The leading Java solution (282) bytes does not give the correct solution. I can't comment on it as I don't have 50 reputation. Is there any way to mark incorrect solutions?

What is the issue with its output?

Output of the 282 byte solution is.....
99 bottles of beer on the wall, 99 bottles of beer.
 Take one down and pass it around, 99 bottles of beer on the wall.
98 bottles of beer on the wall, 98 bottles of beer.
 Take one down and pass it around, 98 bottles of beer on the wall.
.....
2 bottles of beer on the wall, 2 bottles of beer.
 Take one down and pass it around, 2 bottles of beer on the wall.
1 bottles of beer on the wall, 1 bottles of beer.
 Take one down and pass it around, 1 bottles of beer on the wall.
Go to the store and buy some more!

Issues)

When the bottle is taken down and passed around the number of bottles should be one less.
Should be a blank line after each verse
When mentioning 1 bottle, should say bottle, not bottles
The last line should read "Go to the store and buy some more, 99 bottles of beer on the wall."

05AB1E, 167 bytes
Dammit, 167 is waaayyy too long. But this gave me some new ideas to make this a bit shorter.
Code:
99L`[D?ð?“±¥“?D1Q"s "si\ð}?“€‚¬ž€‰€€íÒ, “??ð?“±¥“?D1Q"s "si\ð}?“€‚¬ž.“,Ž“Ÿ†€µ§€€ƒîå€•³†, “ª?D?ð?“±¥“?D1Q"s "si\ð}?“€‚¬ž€‰€€íÒ.
“,]“‚œ€„€€›‰€ƒŽŠ€ä€£, 99Ïê€‚¬ž€‰€€íÒ.“ª,

Try it online!
Uses CP-1252 encoding.

PHP, 226 221 bytes
Minified
<? $w=" on the wall";for($i=99;$i>=0;$i--){if($j&&$i)echo"Take one down and pass it around, $i$j$w.

";$j=" bottle".($i!=1?'s':'')." of beer";if($i)echo"$i$j$w, $i$j.
";}echo"Go to the store and buy some more, 99$j$w.";

Expanded
<?

  $w=" on the wall";

  for($i=99;$i>=0;$i--){
    if($j && $i)
       echo "Take one down and pass it around, $i$j$w.

";

    $j=" bottle".($i!=1?'s':'')." of beer";

    if($i)
       echo "$i$j$w, $i$j.
";
  }

  echo "Go to the store and buy some more, 99$j$w.";

https://ideone.com/m89z3E

We allow the use of the short opening tag (<?) here, though my memory is slipping as to whether or not that requires another command-line argument (and thus only saving 2 bytes rather than 3). Welcome to PPCG!

@mego Short open tags are enabled by default (i.e. if there were no .ini at all) and therefore, at least in my option, should also be allowed by default. relevant meta post

@primo That's right, thanks for refreshing my memory.

thanks for the tips! it seems that many of the php answers here include the full tags, so in the spirit of consistency, I'll leave it. But thanks for posting the meta because I wasn't sure about notices (i.e. $j is uninitialized) but it seems they're ok

A good way to check is to run the script with the -n option to disable the local ini: php -n script.php. If it produces the correct output, it should be considered valid. While I'm here, you can replace the \n with a literal new line to save a few bytes. You might also be able to combine the last two echo statements with a ternary, e.g. echo$i?"string if true":"string if false";.

@primo works fine with php -n. I don't think using a ternary will work because $j needs to be set between the 2 echos in order for the 1st and last entries to be correct

T-SQL, 436 323 bytes
WITH _ AS(SELECT 99B UNION ALL SELECT B-1 FROM _ WHERE B>2)SELECT REPLACE(REPLACE(REPLACE(A,'#',' of beer'),'@',' on the wall'),'!',' bottle')FROM(SELECT REPLACE(REPLACE('^!s#@, ^!s#.
Take one down and pass it around, %!s#@.','^',B),'%',B-1)A FROM _ UNION ALL SELECT'1!#@, 1!#.
Go to the store and buy some more, 99!s#@.')A

you can save a few more by removing a couple of spaces SELECT 99B and ,B-1)FROM.  Also since  bottles of beer is repeated a few times a replace on that will trim a few more.

Some of the replacements aren't going to save anything or will actually cause it to expand since replace is so many characters... I an working on using the XML contact hack to see if I can reduce it more.

not sure I can save enough characters to just break even on the XML hack :(

fixed and added 4 bytes :(

Using this your byte count is 338. single byte for the line feeds

Actually, the 2nd line from the bottom should be "1 bottle of beer", not "1 bottles of beer...", so you still need to tune your answer.

Ahh I see the problem now.  oh well, I dont care to fix it.

Scratch, 252 bytes

(scoring used)
Resets, repeats for 98 cycles, then a modified last block for the last line.
If you allow "1 bottles" in the list, this can be golfed down to 133 bytes.

Nice Scratch answers!

Thanks, I've been warming up to it after leaving it for a year.

@weatherman115 I think mine is better. Also, your output is incorrect; it's one line, the output needs to be two lines per verse. Not very golfed either

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

Try it online!
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 1s followed by bottles of beer.
1
1$'W, 1$'.¶Take one down and pass it around, $'W.¶¶

Now we replace each of those 1s 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 Ws instead of on the walls and the last verse is off.
1+
$.&

This does the unary-to-decimal conversion by matching each run of 1s 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 ss 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.

This is really cool! I should really learn Retina...

@GamrCorps I'll just casually leave this link here...

MIT Scratch 2.0, 184 bytes
Notes: Since this language is graphical, I counted each block/element used, each byte of the strings, and variable invocations as a byte.

Try it online: https://scratch.mit.edu/projects/115797194/#player
Notes: This program does not automatically clear the list after it's run, you have to go to the editor and run the following block:

I have conveniently added it into the project, however it is not part of the program and does not count towards the byte count.

Ruby, 237 (thanks primo!) 227 bytes
99.downto(2){|i|puts "#{i}#{$B=" bottles of beer"}#{$W=" on the wall"}, #{i}#$B,\nTake one down and pass it around, #{i-1}#{i<3?$C=$B.tr('s','') :$B}#$W.\n\n"};puts "1#$C#$W, 1#$C,\nGo to the store and buy some more, 99#$B#$W."

Mostly ungolfed: (old, outdated)
$B="bottles of beer"
$C="bottle of beer"
$W="on the wall"
99.downto(2) { |i|
  puts "#{i} #$B #$W, #{i} #$B,\nTake one down and pass it around, #{i-1} #{i==2 ? $C : $B} #$W.\n\n"}
  puts "1 #$C #$W, 1 #$C,\nGo to the store and buy some more, 99 #$B #$W."

Try it online:
http://codepad.org/ozIpcgPI

Save a few bytes my moving each string definition to its first use: #{i}#{$B=' bottles of beer'}#{$W=' on the wall'}, and #{i-1}#{i==2?$C=' bottle of beer':$B}.

Alternatives for $C: $C=$B.tr('s',''), or assuming you rename $B to $_: ~/s/;$C=$`+$'.

Also i<3 instead of i==2 and you can put actual linefeeds in your strings instead of using the \n escape sequence.

Java 8, 245 bytes
i->{String b=" bottle",o=" of beer",n=" on the wall",t=b+o,w=b+'s'+o,e=".\n";while(--i>0)out.print(i<2?i+t+n+", "+i+t+e+"Go to the store and buy some more, 99"+w+n+e:i+w+n+", "+i+w+e+"Take one down and pass it around, "+~-i+(i<3?t:w)+n+e+'\n');}

Try it online!
Outputs the whole of the lyrics with a trailing newline at the end.
Explanation (slightly ungolfed):
i->{
/*
 * t = " bottle of beer";
 * w = " bottles of beer";
 */
String b=" bottle",o=" of beer",n=" on the wall",t=b+o,w=b+'s'+o,e=".\n";
while(--i>0) // taking advantage of input from lambda statement
    out.print(i<2?i+t+n+", "+i+t+e+"Go to the store and buy some more, 99"+w+n+e
                 :i+w+n+", "+i+w+e+"Take one down and pass it around, "+~-i+(i<3?t:w)+n+e+'\n');
/*
 * if i<2:
 *   print 'n bottles' in singular form and the final part of the lyrics through simple concatenation
 * else:
 *   print 'n bottles' in plural form plus 'n-1 bottles' through the use of the ternary operator and
 *   '~' - a unary operator that, when placed in the order above, decreases the value of i by 1
 */
}

Java, 289 281 279 276 270 268 bytes
interface B{static void main(String[]p){String a=" bottle",b=" of beer",s=a+'s'+b,x=s,c=" on the wall. ";for(int i=99;i>0;)System.out.println(i+x+c+i+x+(i-->1?"\nTake one down and pass it around. "+i:"\nGo to the store and buy some more. 99")+(x=i==1?a+b:s)+c+'\n');}}

Ungolfed:
interface B{
    static void main(String[]p){
        String a=" bottle",b=" of beer",s=a+'s'+b,x=s,c=" on the wall. ";
        for(int i=99;i>0;)
            System.out.println(
                i+x+c+i+x
                +(i-->1?"\nTake one down and pass it around. "+i:"\nGo to the store and buy some more. 99")
                +(x=i==1?a+b:s)+c+'\n'
            );
    }
}

Built on the previous record, shortened by 6 14 16 19 25 27 characters by rearranging stuff.
I don't have the 50 reputation points to edit or comment the existing Java entry. Just joined to share my improvement, not to grind points :) Hope that's ok.
Curious to see how much lower we can still go.

Welcome to the site! You are always allowed to comment suggestions or post your own answer. However our site policy and culture discourages editting other people's answers with golfs unless they are a "Community wiki".

Thanks!
Just to confirm, I tried to comment the existing Java entry on page 1 several times. I always get a "You must have 50 reputation to comment" popup.

Shortened it by 8 + 2 chars, building on the existing ideas.

Down another 3 bytes, by streamlining how the plural is handled.

Another -6 characters.

Further -2 bytes.

Nice work! The punctuation isn't quite right, so I've taken the liberty of making a corrected version (274 bytes). You might be able to golf that down some more.

Thanks! You're correct that there is punctuation difference between the Java output and the original task. I improved the existing Java solutions to make my version comparable to them. For the original output with the comma, your proposal would be a pretty good starting point.

All solutions have to match the challenge requirements, so the other Java solutions that don't match are also incorrect. Thanks for pointing that out. I left comments on them as well.

C++, 289 bytes
Golfed:
#include <cstdio>
void main(){char*s=" bottles of beer",*t=" on the wall",*u=" bottle of beer";for(int i=99;i>1;--i)printf("%i%s%s, %i%s.\nTake one down and pass it around, %i%s%s.\n\n",i,s,t,i,s,i-1,i<3?u:s,t);printf("1%s%s, 1%s.\nGo to the store and buy some more, 99%s%s.",u,t,u,s,t);}

Works in Microsoft Visual Studio. Though some compilers may require that void main be changed to int main and a return value provided. This increases the size to 297:
#include <cstdio>
int main(){char*s=" bottles of beer",*t=" on the wall",*u=" bottle of beer";for(int i=99;i>1;--i)printf("%i%s%s, %i%s.\nTake one down and pass it around, %i%s%s.\n\n",i,s,t,i,s,i-1,i<3?u:s,t);printf("1%s%s, 1%s.\nGo to the store and buy some more, 99%s%s.",u,t,u,s,t);return 0;}

Ungolfed:
#include <cstdio>

void main()
{
    char* s = " bottles of beer";
    char* t = " on the wall";
    char* u = " bottle of beer";

    for(int i = 99; i > 1; --i)
    {
        printf("%i%s%s, %i%s.\nTake one down and pass it around, %i%s%s.\n\n", i, s, t, i, s, (i - 1), (i < 3 ? u : s), t);
    }

    printf("1%s%s, 1%s.\nGo to the store and buy some more, 99%s%s.", u, t, u, s, t);
}

Link to demo.

You don't have to explicitly return from main, return 0 is implicit if you don't. (Only for main, valid in modern C and C++.) See e.g. http://stackoverflow.com/questions/276807/return-0-implicit

Mouse, 244 bytes
99N:(N.0>^N.!" bottle"N.1>["s"]" of beer on the wall, "N.!" bottle"N.1>["s"]" of beer.!"N.1>["Take one down and pass it around"]N.1=["Go to the store and buy some more"]", "N.1-N:N.0=[99]N.0>[N.]!" bottle"N.1=0=["s"]" of beer on the wall.!!")$

Mouse has very limited capabilities in general, though this is particularly apparent in its handling of strings. The stack can contain only integers; any quoted strings encountered are printed to STDOUT immediately. Further, there is no else construct, so each condition must be stated explicitly.
Ungolfed:
99 N:                                  ~ Initialize a counter
( N. 0 > ^                             ~ While N > 0...
  N. ! " bottle" N. 1 > ["s"]          ~ Print N, conditionally pluralize
  " of beer on the wall, "
  N. ! " bottle" N. 1 > ["s"]
  " of beer.!"
  N. 1 > ["Take one down and pass it around"]
  N. 1 = ["Go to the store and buy some more"]
  ", "
  N. 1 - N:                            ~ Decrement the counter
  N. 0 = [99] N. 0 > [N.] !
  " bottle" N. 1 = 0 = ["s"]
  " of beer on the wall.!!"            ~ ! inside a string is \n
)$

Java, 297 291 bytes
class C{public static void main(String[]s){String b=" bottle",o=" of beer",w=" on the wall",l=b+"s"+o,m=b+o,n=".\n",p;for(int i=99;i>0;){p=i+((i>1)?l:m);System.out.print(p+w+", "+p+n+((--i)>0?"Take one down and pass it around, "+i:"Go to the store and buy some more, 99")+(i==1?m:l)+w+n);}}}

My first golf. Saw it at work, but was unable to post until I got home.
Ungolfed:
class C {
    public static void main(String[]s) {
        String b = " bottle", o = " of beer", w = " on the wall",
               l = b + "s" + o, m = b + o, n = ".\n", p;
        for(int i = 99; i > 0;) {
            p = i + ((i > 1) ? l : m);
            System.out.print(p + w + ", " + p + n
                 + ((--i) > 0 ? "Take one down and pass it around, " + i
                     : "Go to the store and buy some more, 99")
                 + (i == 1 ? m : l) + w + n);
        }
    }
}

You can just use a function...

284 bytes

It's been a while, but I just noticed that your answer's output is incorrect: there should be an extra blank line between verses. Adding a +"\n" fixes it (or you could make the same modification to ceilingcat's suggested golf).

DStack, 265 bytes
@0
# bottles of beer on the wall, # bottles of beer.
Take one down and pass it around, $ bottles of beer on the wall.

@
@1
1 bottle of beer on the wall, 1 bottle of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
@
01SSd99CCcscDtTasd9ttkt

PHP, 248 bytes (with pluralisation)
$b=99;$x=function($n){return"$n bottle".($n!=1?'s':'')." of beer";};$y=" on the wall";do{echo"{$x($b)}$y, {$x($b)}.\n";echo--$b?"Take one down and pass it around":"Go to the store and buy some more";$b||$b=99;echo", {$x($b)}$y.\n\n";}while($b<99);

readable:
<?php

$b=99;
$x=function($n){return"$n bottle".($n!=1?'s':'')." of beer";};
$y=" on the wall";
do{
    echo"{$x($b)}$y, {$x($b)}.\n";
    echo--$b?"Take one down and pass it around":"Go to the store and buy some more";
    $b||$b=99;
    echo", {$x($b)}$y.\n\n";
}while($b<99);

NOTE: previoius version was 197 bytes but didnt correctly show singular "bottle". see history if interested

See whether string interpolation instead of concatenation would help: echo $b.$c.", ".$b.$d.".\n"; → echo "$b$c, $b$d.\n";.

@manatwork: cool didnt think of that!

Just some quick changes, trying to keep the base idea: $b=99;$d=" bottles of beer";$c="$d on the wall";do{echo"$b$c, $b$d.\n",--$b?"Take one down and pass it around":"Go to the store and buy some more";$b||$b=99;echo", $b$c.\n\n";}while($b<99);. (You can find more tricks to apply in Tips for golfing in PHP.)

Sorry dude but at the end this still says "bottles" i.e. 1 bottles of beer on the wall - should be "bottle"

There is an extra newline at the end.

PHP, 267 bytes
<?php $i=99;$t="Take one down and pass it around, ";$b=" bottles of beer";$o=" on the wall";echo"99$b$o, 99$b.\n";while(--$i>1)echo"$t$i$b$o.\n\n$i$b$o, $i$b.\n";$B=str_replace('s','',$b);echo"$t$i$B$o.\n\n$i$B$o, $i$B.\nGo to the store and buy some more, 99$b$o.";

Readable version, with comments:
<?php

$i=99;
$t="Take one down and pass it around, ";
$b=" bottles of beer";
$o=" on the wall";

// first one
echo"99$b$o, 99$b.\n";

// decrement from 98 to 2
while(--$i>1) echo"$t$i$b$o.\n\n$i$b$o, $i$b.\n";

// ^^ "Take one down and pass it around, $i bottles of beer on the wall.\n\n ^^
// $i bottles of beer on the wall, $i bottles of beer.\n"

// last one
$B=str_replace('s','',$b); // change "bottles" to "bottle"
echo"$t$i$B$o.\n\n$i$B$o, $i$B.\nGo to the store and buy some more, 99$b$o.";

Go, 287
package main
import."fmt"
func main(){p,s,n:=Printf,"%[1]d bottle%[2]s of beer",`.
`
o:=s+" on the wall"
a,b,c,i,k:=o+`, `+s+n,`Take one down and pass it around, `+o+n+`
`,`Go to the store and buy some more, `+o+n,99,"s"
for 1<i{p(a,i,k)
if i--;i<2{k=""}
p(b,i,k)}
p(a,i,k)
p(c,99,"s")}

There is probably room for further optimization

Ceylon 1.2, 359
alias S=>String;alias I=>Integer;shared void run(){value[b,o,w,t,d]=[" bottle"," of beer"," on the wall","Take one down and pass it around, ",".\n"];S l="1"+b+o;S h(I n)=>"``n``"+b+"s"+o;S f(I n)=>h(n)+w+", "+h(n);S c(I n)=>t+h(n)+w+d+"\n"+f(n);print(d.join{f(99),*(98..2).map(c)}+d+t+l+d+"\n"+l+w+", "+l+d+"Go to the store and buy some more, "+h(99)+w+".");}

Some golfing tricks: Shorter aliases for String and Integer, and the tuple-decomposition-declaration which saves us some S and = (that was only introduced in Ceylon 1.2).
The rest is straightforward extraction of common parts to variables/functions.
Formatted:
// 99 bottles of beer
//
// Question:  http://codegolf.stackexchange.com/q/64198/2338
// My answer: http://codegolf.stackexchange.com/a/64522/2338
alias S => String;
alias I => Integer;
shared void run() {
    value [b, o, w, t, d] = [
        " bottle",
        " of beer",
        " on the wall",
        "Take one down and pass it around, ",
        ".\n"
    ];
    S l = "1" + b + o;
    S h(I n) => "``n``" + b + "s" + o;
    S f(I n) => h(n) + w + ", " + h(n);
    S c(I n) => t + h(n) + w + d + "\n" + f(n);
    print(d.join { f(99), *(98..2).map(c) }
                + d + t + l + d + "\n" + l + w + ", " + l + d +
                "Go to the store and buy some more, " + h(99) + w + ".");
}

Microsoft Small Basic, 313 bytes
g=" of beer"
a=" bottles"+g
b=" on the wall"
c="Take one down and pass it around, "
d="."
e="1 bottle"+g
f=", "
i=99
n=Text.GetCharacter(010)
While i>1
TextWindow.WriteLine(i+a+b+f+i+a+d+n+c+(i-1)+a+b+d+n)
i=i-1
EndWhile
TextWindow.WriteLine(e+b+f+e+d+n+"Go to the store and buy some more, 99"+a+b+d)

Grond, 226 chars
p=".";b=" bottle";B=b+'s';o=" of beer";w=" on the wall";i=100;w(--i>0){T=i<3?b:B;h=B+o+w;u=i<2?b+o:B+o;k=i+u;a(k+w+', '+k+p);a(i>1?"Take one down and pass it around, "+(i-1)+T+o+p:"Go to the store and buy some more, 99"+h+p);}

Grond is a new programming languege that I am currently developing. It aims to make Javascript shorter to write, while still being "Javascript". I'm pretty mad that I didn't beat any of the Javascript answers, but the languege is still beta and I am still a n00b. Oh and buy the way, I might not call it grond in the future, but it is a reference to the hammer of the underworld.
Actually, Grond can beat some of the JS answers @ 179 chars, as follows:
f(x=99,z=(a=' on the wall')=>`${x||99} bottle${1-x?'s':''} of     beer`+a;x;)a(z()+', '+z`.
`+(--x?'Take one down and pass it around, ':'Go to the store and buy   some more, ')+z()+'.')

But that is copying Adam Dally's answer, so I won't do it (unless you all want me to).
Anyway, here is the compiler:

String.prototype.reverse = function() {
  return this.split("").reverse().join("");
}
var source = "p='.';b=' bottle';B=b+'s';o=' of beer';w=' on the wall';i=100;w(--i>0){T=i<3?b:B;h=B+o+w;u=i<2?b+o:B+o;k=i+u;a(k+w+', '+k+p);a(i>1?'Take one down and pass it around, '+(i-1)+T+o+p:'Go to the store and buy some more, 99'+h+p);}";
var wc = source; // wc stands for working copy
var lastWC;
while (true) {
  lastWC = wc;
  wc = wc.reverse();
  wc = wc.replace(/\(a(?!\w)/, "(trela"); //alert ,a
  wc = wc.replace(/\(f(?!\w)/, "(rof"); // for, f
  wc = wc.replace(/\(w(?!\w)/, "(elihw"); //while,w
  wc = wc.replace(/\(p(?!\w)/, "(tpmorp"); //prompt,p
  wc = wc.replace(/\(vr\./, "(esrever."); //reverse, rv
  wc = wc.replace(/\(tm\./, "(chtam."); // match, mt
  wc = wc.replace(/\(nj\./, "(nioj."); //join, .jn
  wc = wc.replace(/\(pr\./, "(ecalper."); //replace, .rp
  wc = wc.replace(/\(ls\./, "(.ecils"); //slice, sl
  wc = wc.replace(/\(lt\./, "(esaCrewoLot."); //toLowerCase, tl
  wc = wc.replace(/\(ut\./, "(esaCreppUot."); //toUpperCase, tu
  wc = wc.reverse();
  wc = wc.replace(/\.l(?!\w)/, ".length"); // .lenght, .l
  if (lastWC === wc)
    break;
}

// quine builtin commented out because it messes things up
wc = " String.prototype.reverse=function(){return this.split('').reverse().join('');} \n var h = ['hello, world', 'Hello, World', 'Hello, World!', 'hello, world!', 'hello world', 'hello world!', 'Hello World', 'Hello World!', 'Hello, world.', 'Hello, World.' ,'hello world.' , 'Hello World.']; /* var q ='" + source + "';*/ " + wc;

eval(wc);

This reminds me a lot of goruby. If you could generalize this to expand any missing function/keyword to its closest match, I think you might really have something.

@primo that is what I plan on doing

PHP, 269 263 260 bytes
function f($p){return" bottle".($p-1?"s":"");}function b($b){$o=" of beer";$w=" on the wall";while($b){$n=f($b);echo"
$b$n$o$w, $b$n$o.
";echo--$b?"Take one down and pass it around, $b":"Go to the store and buy some more, 99";$n=f($b);echo"$n$o$w.
";}}

Readable version:
function f($p) {
    return " bottle" . ($p-1 ? "s" : "");
}

function b($b) {
    $o = " of beer";
    $w = " on the wall";
    while ($b) {
        $n = f($b);
        echo "
$b$n$o$w, $b$n$o.
";
        echo --$b ? "Take one down and pass it around, $b"
                  : "Go to the store and buy some more, 99";
        $n = f($b);
        echo "$n$o$w.
";
    }
}

Call b(99) and away you go!
Almost certainly could use some more optimization.

S.I.L.O.S, 360 bytes
a=97
def q print z bottle v beer L Line g wall x IntNoLine h the c around
qL 99 zs of v on h g, 99 zs of v.
lbls
q Take one down and pass it c,
a+1
qx a
qL  zs of v on h g.
qx a
q  zs of v on h g,
qx a
qL  zs of v.
a-2
if a s
qL Take one down and pass it c, 1 z of v on h g.
qL 1 z of v on h g, 1 z of v.
q Go to h store and buy some more, 99 zs of v on h g.

Try It Online!

love it! Have an upvote upgoat!

SOGL 0.8.2, 123 bytes
³0=?"¡θ9‘}"⁵εα⁸‘⁴1≠? s+}+→@MHM{@⁽t⁾"¹u?«◄/⅞┐⌡u¹E³‘oo"ā)≡⁰≤τ¹‘o"5|░H׀│P¬2h⁶─#‘⁴0=?M"Zø‼;3σΠg℮╥╝ņΟXž∆‘}⁽OH@o"Θ┘θΖγ┘ψψ╚⅛╔⁷‘oøO

First of all, the function:
                             this function is ran with a number on the stack - the bottle count and pushes "x/no more bottle(s)"
³0=?"¡θ9‘}"⁵εα⁸‘⁴1≠? s+}+→@  full; example here is 0
                         →@  replace "@" in the further code with the code below              [0]
³                            triplicates the number                                           [0, 0, 0]
 0=?     }                   if equal to 0                                          stack ->  [0, 0, 1]
    "¡θ9‘                      push "no more" (this can leave garbage)                        [0, 0, "no more"]
          "⁵εα⁸‘             push " bottle"                                                   [0, 0, "no more", " bottle"]
                ⁴            duplicate the 2nd from top of the stack (number or "no more")    [0, 0, "no more", " bottle", "no more"]
                 1≠?   }     if not equal to 1 then                                           [0, 0, "no more", " bottle"]
                     s+        append "s"                                                     [0, 0, "no more", " bottles"]
                        +    add together                                                     [0, 0, "no more bottles"]

And the rest of the code:
MHM{@⁽t⁾"¹u?«◄/⅞┐⌡u¹E³‘oo"ā)≡⁰≤τ¹‘o"5|░H׀│P¬2h⁶─#‘⁴0=?M"Zø‼;3σΠg℮╥╝ņΟXž∆‘}⁽OH@o"Θ┘θΖγ┘ψψ╚⅛╔⁷‘oøO
MH                                                                                push 100-1 = 99 (bottle count)
  M{                                                                                repeat 100 times
    @                                                                                the function defined previously
     ⁽t⁾                                                                                output with the 1st letter uppercase (doesn't pop, newline)
        "¹u?«◄/⅞┐⌡u¹E³‘o                                                                          append " of beer on the wall, "
                        o                                                                         append the previous functions result
                         "ā)≡⁰≤τ¹‘o                                                               append " of beer."
                                   "5|░H׀│P¬2h⁶─#‘                                                push "take one down and pass it around, "
                                                  ⁴0=?                   }                        if the original bottle count = 0
                                                      M                                           push 100 (new bottle count)
                                                       "Zø‼;3σΠg℮╥╝ņΟXž∆‘                         push "go to the store and buy some more, "
                                                                          ⁽O                      output with the 1st letter uppercase (hardcoding would've been longer)
                                                                            H                     decrease the bottle count
                                                                             @o                   run that function and output its result
                                                                               "Θ┘θΖγ┘ψψ╚⅛╔⁷‘o    append " of beer on the wall."
                                                                                øO  output an empty line

could probably be golfed 1-3 bytes removing repetition like " of beer on the wall, " and " of beer on the wall."

Prolog (SWI), 312 bytes
w(X):-write(X).
e:-w('.'),nl.
t(X):-w(X),b(X).
b(B):-B>1,w(' bottles of beer').
b(B):-B=1,w(' bottle of beer').
c(B):-t(B),w(' on the wall').
r(N):-N=0,w('Go to the store and buy some more, '),c(99),e.
r(N):-N>0,w('Take one down and pass it around, '),c(N),e,nl.
s(X):-X>0,c(X),w(', '),t(X),e,N is X-1,r(N),s(N).

Try it online!

Welcome to the site! Couild you maybe create an online demo of your answerr using Try it Online?

Why does your output end with false.\n\n\n?

PowerShell, 228 227 220 221 bytes
$a,$l,$e,$s=" bottles of beer"," bottle of beer"," on the wall","Take one down and pass it around, "
99..3|%{"$_$a$e, $_$a.
$s$($_-1)$a$e.
"}
"2$a$e, 2$a.
$s`1$l$e.

1$l$e, 1$l.
Go to the store and buy some more, 99$a$e."

Try it online!
Psssh, who needs fancy-pants logic. (Spoilers: Better answers do) This just manually builds the last two lines instead of doing clever stuff. Also, saved 7 bytes by replacing `n with an actual line break. Lost a byte because I forgot to punctuate.

C++, 288 289 282 bytes
#import<iostream>
int i,v;main(){for(i=99;i>0;i--)printf("%1$d%6$s%2$s%7$s%8$s, %1$d%6$s%2$s%7$s.\n%3$s, %4$d%6$s%5$s%7$s%8$s.\n\n",i,i-1?"s":"",i-1?"Take one down and pass it around":"Go to the store and buy some more",v=i-1?:99,v-1==1?"":"s"," bottle"," of beer"," on the wall");}

Requires no additional compile flags.
Ideone: https://ideone.com/fhL9Lv

267 bytes

Assembly (MIPS, SPIM), 676 623 575 570 bytes
.data
a: .asciiz " bottle"
c: .asciiz " bottles"
e: .asciiz " of beer.\n"
f: .asciiz " of beer on the wall, "
g: .asciiz " of beer on the wall.\n"
h: .asciiz "Take one down and pass it around, "
i: .asciiz "Go to the store and buy some more, "
.text
.globl main
main:
li $8 99
li $11 1
y:
la $9 f
jal t
la $9 e
jal t
addi $8 -1
beq $8 $0 v
la $4 h
syscall
la $9 g
jal t
li $2 11
li $4 10
syscall
j y
t:
move $4 $8
li $2 1
syscall
li $2 4
beq $8 $11 q
la $4 c
j r
q:
la $4 a
r:
syscall
move $4 $9
syscall
jr $ra
v:
la $4 i
syscall
li $8 99
la $9 g
jal t
li $2 10
syscall

Try it online!
Turns out it's actually cheaper to declare "bottle" and "bottles", as well as strings with newlines and commas directly in place, and just select between them. Also, using all the syscalls is actually cheaper than the alternative, though only because the the call pointer register is hidden. MIPS' need for newlines actually makes this surprisingly readable, if you know the opcodes. For those of you trying to decipher it, t is a function to print the variations of " bottle[s] of beer...," and $9 stores the address of the string to finish that phrase. One annoying caveat was the fact that all the verses require a newline between them, but the end cannot have two newlines (afaik). So theres a section at the end of the loop that prints a single character, which happens to be newline (decimal 10).
Edit: saved 5 bytes by using $9 instead of $10. I had originally used $9, but optimized it out, and forgot to shift the registers optimally. Also, this submission took forever because I was editing it in place.

486 with some parser abuse

Wren, 246 bytes
System.print((99..1).map{|i|"~ on the wall, ~.\n%(i>1?"Take one down and pass it around":"Go to the store and buy some more"), %(i>1?i-1:99) bottle%(i!=2?"s":"") of beer on the wall.".replace("~","%(i) bottle%(i>1?"s":"") of beer")}.join("\n\n"))

Try it online!

C#, 205 bytes
()=>{for(int i=299;i-->2;)System.Console.Write(@$"{i/3:0;;99} bottl{i/3^1:es;;e} of beer{i%3-3/i: on the wall;\.
Go to the store and buy some more;\.
Take one down and pass it around}{~i%3:;\, ;\.

;}");};

Try it online!
As an anonymous lambda. A full program scores 227.
Putting custom numeric formats to good use. I'm particularly satisfied with i%3-3/i to select between Take one down, Go to the store, and on the wall.

APL (Dyalog Unicode), 200 bytes
-18 thanks to Razetime
d←'.'
q←'s',⍨w←'bottle'
x←∊a z←' of beer' ' on the wall'
{⎕←∊⍵q x','⍵q a d⋄⎕←∊'Take one down and pass it around,'(⍵-1)w('s'/⍨⍵≠2)x d}¨⌽1↓⍳99
∊1w x','1w a d
∊'Go to the store and buy some more,'99q x d

Try it online!

"98 bottle of beer"?

@Adám I messed up not equals and equals; it should work now

You can golf a lot more here. x←∊a z←' of beer' ' on the wall' and use ∊ in place of many concatenations. Also explore the default output format when you mix raw numbers and text (it'll put spaces around the numbers ― just what you need).

@Adám thanks for the tip, I'll look into it tomorrow

@Adám I attempted some golfs; it seems a whole lot smaller.

(based on Adám's comment) You can take the 1's and the 99 in the last line out of the strings like this: ⍞←∊1 w x','1w a k'Go to the store and buy some more,'99q x d.

Excel, 198 209 206 bytes
+9 bytes for correcting 1 bottle error
-3 bytes for tweaks
=LET(q,99-ROW(1:99),b,q+1&" bottle"&REPT("s",q>0)&" of beer",w,b&" on the wall",w&", "&b&".
"&IF(q,"Take one down and pass it around, "&INDEX(w,100-q),"Go to the store and buy some more. "&INDEX(w,1))&".
")

Link to Spreadsheet
This version shows 3 lines per cell.  Would be slightly longer to put it all in one cell and wouldn't be completely readable on screen.
Original
=LET(q,100-ROW(1:98),b," bottles of beer",w,b&" on the wall",CONCAT(q&w&", "&q&b&".
Take one down and pass it around, "&(q-1)&w&".

")&"1"&b&", 1"&b&".
Go to the store and buy some more, 99"&w &".")

This can be made 3 bytes shorter ((q-1)→q-1 and &w &→&w&) but it also has an error on the end: The last line starts "1 bottles of beer," instead of "1 bottle of beer on the wall," Try this as the 4th line in the formula: ")&"1"&SUBSTITUTE(w,"s",)&", 1"&b&". I count 212 bytes with all those changes.

(Scratch that last line suggestion. I only fixed the first instance of 1 bottles but you get the idea.)

Well that's unfortunate.  I rather liked my code being a multiple of 99.

Using Rept instead of If here is a wonderful trick - you should add that to the tips page

@TaylorAlexRaine I don't get how REPT is a good trick when IF(q,"s","") is one byte shorter than REPT("s",q>0). Also, @Axuary, there's a small error in the last line. It has buy some more.  but it should be buy some more,  with a comma.

Vyxal j, 145 bytes
2₁rƛ«ƛ
÷v≬ẏẆ:βĠPd⁰1Ȧ∩_«£¥:½h\r+WḢnð+vJ‛, j\.¶`¬Ȧ λḭ ∧ċ λ¬ ∆¶ it ⟑Ṡ, `n1-ð¥\.Wṅ;Ṙ`1 ḋ¶ `¥⌈ḢṄ+:½h`er. `+"‛, j`Go to λλ ¬↓ λ¬ by λ⋎ λƈ, 99 `¥+\.+"⁋J

Try it Online!

There are seven answers above you already. Please don't include any comments like "beats all other answers" or "ties with X/Y/Z", which can be invalidated at any moment, and do not align well with our view of intra-language competitions.

@Bubbler very sorry

Regenerate, 170 bytes
((99( bottle)s( of beer))( on the wall)), $2.\n(Take one down and pass it around, (((${$9-1}|98)$3s{1-1/$9}$4)$5).\n\n$7, $8.\n){98}Go to the store and buy some more, $1.

Try it here!
This is basically just a bunch of capturing groups and backreferences repeated 98 times, with two interesting parts:

Group 9, which holds the number at each iteration, is (${$9-1}|98). The first time through, since group 9 hasn't been matched yet, the $9 backreference fails and we use the other option, 98. Afterwards, $9 contains the previous number, so we use that value minus 1: ${$9-1}.
In order not to pluralize bottle when the number hits 1, we use s{1-1/$9}. Since / is integer division, 1/$9 will be 0 if group 9 is greater than 1 and 1 if group 9 equals 1. Then {1-1/$9} repeats the s 1 time if the number is over 1 and 0 times if the number equals 1.

TypeScript’s type system, 278 bytes
This was the first golf I ever submitted using TS types! It's been over a year since I made my original 591 byte solution, and I'm glad to say I have gotten way better at TS types golf. Revisiting the 99 bottles of beer problem, I came up with the following solution, which is 60 bytes shorter than the solution ASCII-only wrote based on mine, and over half the size of the original solution I had when I first approached the problem.
//@ts-nocheck
type F<N=[1],S="Go to the store and buy some more, 99 bottles of beer on the wall.",L=N["length"],X=`${L} bottle${L extends 1?"":"s"} of beer`,Z=" on the wall",V=`${X}${Z}, ${X}.
${S}`>=L extends 99?V:F<[...N,1],`Take one down and pass it around, ${X}${Z}.

${V}`>

Try it at the TypeScript playground!
This is a type F which is generic, but all the generic type parameters have defaults so no input types are needed.
Explanation: Start with the last line of the text, and N = 1:
Go to the store and buy some more, 99 bottles of beer on the wall.

Until N = 99, increment N and prepend:
Take one down and pass it around, {N} bottles of beer on the wall.

{N} bottles of beer on the wall, {N} bottles of beer.

When N = 99, prepend:
99 bottles of beer on the wall, 99 bottles of beer.

and return the final string.
In the actual program, repeated parts of the text are extracted into types that are then embedded into the string type.
For posterity's sake, here's my old solution, although I'm a bit embarassed about how bad I used to be :)
TypeScript Type System, 591 344 bytes
344 bytes thanks to @ASCII-only
type m
=A['length']extends 100?A:m
type n=m
type d<N>=n[N]
type a<N>=`${N} bottle${N extends 1?"":"s"} of beer`
type _<N extends 0=99>=`${a<N>} on the wall, ${a<N>}.
${N extends 1?`Go to the store and buy some more, ${a<99>} on the wall.`:`Take one down and pass it around, ${a<d<N>>} on the wall.

${_<d<N>>}`}`

TS Playground link
Also I'm not sure this is valid, since it doesn't output the type to STDOUT, since that's not a thing with TS types.

308?

@ASCII-only I initially thought of something like that when I posted the answer, but the problem is that the type isn't the full lyrics, it ends with 51 bottles of beer... it around, ${..., while the version I posted has the full lyrics in the type.

hmmm... then something like this should work for 578

344? (full lyrics ofc)

@ASCII-only Nice, thanks

*isn't it 344 bytes? also you might want to re-add the test case - and optionally add a comment saying that readers could test by removing a character (or editing) the end of the string x

and/or switch to using type IsEqual<X, Y> = (<T>() => T extends X ? true : false) extends (<T>() => T extends Y ? true : false) ? true : false

like this: https://tsplay.dev/mp8O7m (except you should probably paste the full link, not the shortened one of course)

@ASCII-only Fixed, thanks for the help golfing.

@ASCII-only I revisited this problem today and came up with a much shorter solution :D

Matlab, 254
b=@(n)[' bottle','r'+eye(n~=1),' of beer'];o=' on the wall';f=@fprintf;
for k=99:-1:1;f(['%d',b(k),o,', %d',b(k),'.\n'],k,k);
if k>1;f('Take one down and pass it around, %d',k-1);
else;f('Go to the store and buy some more, 99');
end;f([b(k-1),o,'.\n\n']);end

Groovy, 281 bytes
def s=" of beer on the wall";def b=" bottles";for(int i=99;;)(i>1)?{println i+b+s+", "+i+b+" of beer.\nTake one down and pass it around, "+--i+(i==1?" bottle":b)+s+".\n"}():{println "1 bottle"+s+", 1 bottle of beer.\nGo to the store and buy some more, 99"+b+s+".";System.exit(0)}()

Try it online.
dis is best idea

Vitsy is still winning, though. ;) Keep golfing. +1

Nice, but this is 213 bytes. (Yes, I know it's a bit late, so what?)

@JAtkin This was an incredibly lazy reference implementation to my sandboxed version of this challenge.

Oh well ;) I can see it now that I am looking at the code. (redundant def, ...)

C, 244 242 240 bytes
k;main(i){char*B=" bottle",*G="s of beer",*W=" on the wall";for(i=99;i;k=--i<2)printf("\n%d%s%s%s, %d%s%s.\n%s, %d%s%s%s.\n",i,B,G+k,W,i,B,G+k,k?"Go to the store and buy some more":"Take one down and pass it around",k?99:i-1,B,G+!(i-2),W);}

Compiled as:
gccw -std=c99 -o party party.c

Multiline:
k;
main(i)
{
    char *B=" bottle",
         *G="s of beer",
         *W=" on the wall";

    for(i = 99; i; k = --i < 2)
        printf("\n%d%s%s%s, %d%s%s.\n%s, %d%s%s%s.\n",
               i,B,G+k,W,
               i,B,G+k,
               k ? "Go to the store and buy some more":
                   "Take one down and pass it around",
               k ? 99 : i-1, B, G + !(i-2), W
        );
}

C, 249 bytes
#define A(N)printf(N<2?"%s%d bottle%s of beer%s":N^2?N^4?", ":n?"Take one down and pass it around, ":"Go to the store and buy some more, ":".\n\n","",n?n:99,n>9|!n?"s":"",N ?" on the wall":".\n"),
n=99;main(){while(n)A(1)A(3)A(0)--n,A(4)A(1)A(2)0;}

Ungolfed:
#define A(N) \
    printf( \
        /* format string */ \
        N<2 \
            ? "%s%d bottle%s of beer%s" \
            : N^2 \
                ? N^4 \
                    ? ", "  \
                    : n ? "Take one down and pass it around, ":"Go to the store and buy some more, " \
                : ".\n\n", \
        /* arguments (ignored if N >= 2) */ \
        "", \
        (n ? n : 99), \
        (n > 9 | !n) ? "s":"", \
        N ?" on the wall":".\n"),

n=99;
main() {
    while(n) A(1) A(3) A(0) --n, A(4) A(1) A(2) 0;
}

Swift 2.0, 208 239 bytes
After fixing the bottles thing, it's a little longer:
let a:Int->String={"\($0) bottle\($0==1 ?"":"s") of beer"},b=" on the wall"
(1...99).reverse().forEach{print("\(a($0)+b), \(a($0)).\n\(($0==1 ?"Go to the store and buy some more, "+a(99):"Take one down and pass it around, "+a($0))+b).\n")}

Here's the old, shorter version too without handling the s:
let a=" bottles of beer",b=" on the wall",c="Go to the store and buy some more, 99"
(1...99).reverse().forEach{print("\($0)\(a+b), \($0)\(a).\n\(($0==1 ?c:"Take one down and pass it around, \($0)")+a+b).\n")}

JavaScript ES5, 233 bytes
for(i=100,a=' bottle',b=' of beer',c=' on the wall';i>1;i--,alert(i+a+(i!=1?'s':'')+b+c+', '+i+a+(i!=1?'s':'')+b+'.'+(i>1?'Take one down and pass it around, '+(i-1):'Go to the store and buy some more, '+99)+a+(i==2?'':'s')+b+c+'.'));

Bash, 235 bytes
e=of\ beer
d="Take one down and pass it around"
c=" on the wall"
b=" bottles "
f=$b
for n in {99..2};{
((n==2))&&f=${f/s}
echo "$n$b$e$c, $n$b$e.
$d, $[n-1]$f$e$c.
"
}
echo "1$f$e$c, 1$f$e.
Go to the store and buy some more, 99$b$e$c."

Readable
e="of beer"
d="Take one down and pass it around"
c=" on the wall"
b=" bottles "
f=$b
for n in {99..2}; {
  ((n==2)) && f=" bottle "
  echo -e "$n$b$e$c, $n$b$e.\n$d, $[n-1]$f$e$c.\n"
}
echo -e "1$f$e$c, 1$f$e.\nGo to the store and buy some more, 99$b$e$c."

As @mego mentions in the comments below, you can easily try it and see the result here: http://ideone.com/6088aJ
Update:
I have used @manatwork hints. The readable version is a little bit different from the short version for better understanding.

This fails to print "bottles" in "Take one down and pass it around, n bottles of beer on the wall." for n > 1.

As ; and the CR are both 1 byte, there is no benefit in posting Bash solution on a single line. Please split it on separate lines to improve readability. (You may find some trick to improve it in Tips for golfing in Bash.)

@Mego I'm sorry. I added f=$b for fixing this

@manatwork Thank you for the link. I have replaced the seq command and the do and done by brackets. Furthermore I have added a better readable version.

Looks good now, +1. I'd recommend adding this link to your answer so everyone can easily try it.

A few minor things: s=s seems completely unused; e="of beer" → e=of\ beer; $((n-1)) → $[n-1]; instead of \n use a literal line wrap in the string literal, so you can remove the -e too; [ $n = 2 ] → ((n==2)).

Calculating again, there is a waste of space. Literally. I mean, you should move the spaces from the echo parameters to the text pieces declarations. I mean like this: http://pastebin.com/TGsH5QVZ

Sorry, but this throws syntax error. There must be a command separator in front of }. Either ; or CR.

Oh, sorry. Copy paste problem ;-)

Python3, 258 237 232 bytes
Being strict about spaces and newlines.
i=98;b="bottle";w="on the wall";o="of beer"
while i+1:y=bool(i);s="s"*y;print(i+1,b+s,o,w+",",i+1,b+s,o+".\n"+("Go to the store and buy some more,","Take one down and pass it around,")[y],i or 99,b+"s"*bool(i-1),o,w+"."+"\n"*y);i-=1

Readable (sort of):
i=98
b="bottle"
w="on the wall"
o="of beer"
while i + 1:
    y = bool(i)
    s = "s" * y
    print(
        i + 1,
        b + s,
        o,
        w + ",",
        i + 1,
        b + s,
        o + ".\n"
        + (
            "Go to the store and buy some more,",
            "Take one down and pass it around,"
        )[y],
        i or 99,
        b + "s" * bool(i - 1),
        o,
        w + "."
        + "\n" * y
    )
    i -= 1

Default unspecified arguments for print(): sep=' ' and end='\n'.

golflua, 206 bytes
\b(t)~i.." bottle"..(i>1&"s"|"").." of beer"..t$\c(t)~b" on the wall"..t$i=99@i>0w(c", "..b".")?i<2 ~~$i=i-1w("Take one down and pass it around, "..c".\n")$i=99w("Go to the store and buy some more, "..c".")

Sample run:
bash-4.3$ golflua 99.golflua | tail
Take one down and pass it around, 3 bottles of beer on the wall.

3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.

2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.

1 bottle of beer on the wall, 1 bottle of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.

O 1.2, 169 bytes
{.o" bottle"o.1>{'so}{}?" of beer"o}:b{" on the wall"o}:w9B*..{bw", "o b "."p(.{"Take one down and pass it around, "}{;"Go to the store and buy some more, "}?obw'.p""p}d

(Works with the old Java interpreter. No idea whether is matter of version of interpreter, but the online O Language IDE throws error.)
Sample run:
bash-4.3$ java xyz.jadonfowler.o.O 99.o | tail

3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.

2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.

1 bottle of beer on the wall, 1 bottle of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.

Groovy, 213 bytes
Quite simple, probably much more golfing could be done.
b={" $it bottles of beer.\n"}
print ((99..1).collect{"$it bottle${it>1?'s':''} of beer on the wall,"+b(it)+(it>1?"Take one down and pass it around,"+b(it-1):"Go to the store and buy some more,"+b(99))}.join('\n'))

CJam, 252 227 220 bytes
I can guarantee that this is not the shortest solution. Oh well, I tried.
" bottles of beer on the wall.

":D;99,W%:){_"%d bottles of"D28/0=+", %d bottles of beer."+e%W$e%{'s=1$1=&!},\_(:M{"
Take one down, pass it around, %d"\(e%D{'s=M1=&!},}{"
Go to the store and buy some more, 99"D+}?+1-+o}%

Try it out here. Any golfing tips are greatly appreciated.
Thanks to @Dennis for helping me shorten this!

Bubblegum, 474 bytes
00000000: e02de601d25d001ce07cc46f3a26b205c864c02a139d  .-...]...|.o:&...d.*..
00000016: 166e795c307826823b392997b956fbc8b6c350ce248e  .ny\0x&.;9)..V....P.$.
0000002c: 1071a7cb6e24ab42a254bbe0a8be43ad25d657dd967a  .q..n$.B.T....C.%.W..z
00000042: 258b807b20b4ceba00a9918ae3e3eed4e23bb85856ef  %..{ ............;.XV.
00000058: e1267f9a415e5ca27213fcf0adf089c56057787ec1fe  .&..A^\.r.......`Wx~..
0000006e: 3620203a8af3c18b3cc36c43f04307eed88938663114  6  :....<.lC.C....8f1.
00000084: 4de9bb22d200d500dfaae3b7c6eda8da60c7ad0e3496  M.."............`...4.
0000009a: d5df645de0ba1a202c6094f9103fc864a170d4c001d3  ..d]... ,`...?.d.p....
000000b0: d862374c5d4918d17d84d30c1234bb7861a1b3545599  .b7L]I..}....4.xa..TU.
000000c6: 4daaab8a872177daef7ee025d5ec84c4ba56f0840d8f  M....!w..~.%.....V....
000000dc: c49023d4506350828b8bfa6287c90b9cdac45b11283f  ..#.PcP....b......[.(?
000000f2: 9dd5926301a37d1817468471f0e5f5c38999e5cd9d9b  ...c..}..F.q..........
00000108: 3429c1755fd57139c9c66126f899217c3b09395dd205  4).u_.q9..a&..!|;.9]..
0000011e: 6a0332c73adbe8015922be7407dd21f813b1d90a389b  j.2.:...Y".t..!.....8.
00000134: d0bc6aac22cc65bdb8dbdd03d863654f06538750c04b  ..j.".e......ceO.S.P.K
0000014a: e437a1e0f19dbb0d5d598f3122a31f400245f39c156a  .7......]Y.1"..@.E...j
00000160: 06082aaa40ce6f0c822f585b6d1053a8144cf5552831  ..*.@.o../X[m.S..L.U(1
00000176: 3885ffc5d3aa540bc74534253b94406174b4b4e0337d  8.....T..E4%;.@at...3}
0000018c: 607f28b1614429ee7238ecba8f8682446140cbc507f9  `.(.aD).r8.....Da@....
000001a2: 54e2cbed802442331930e963822df3527911b0f42895  T....$B3.0.c.-.Ry...(.
000001b8: e0a1a806829eca36fe4bb623dbc6b87c45c26843f770  .......6.K.#...|E.hC.p
000001ce: ede402da278009d9a19a0000                      ....'.......

The above hexdump can be reversed with xxd -r -c 22.

BitShift, 342372 bytes
Pre-note: This is probably sub-optimal...
BitShift is a language created by me, which only supports bit-shifting operations on a single value. It is not great for code-golf :')
Since the program is too long to include in this post, it is provided here.
It can be tested here. Be aware that it will take a while to finish.
I am hoping for this challenge to provide a better solution to this problem.

if you click "make readable" it gets mad because newlines aren't 1 or 0 haha

R, 233 Bytes
f=function(n){b="bottles of beer";p=paste0;w="on the wall";cat(n,b,p(w,","),n,p(b,"."),"\n");if(n>1){cat("Take one down and pass it around,",n-1,b,p(w,".\n"));f(n-1)}else cat("Go to the store and buy some more, 99",b,p(w,"."))}
f(99)

Ideone as a proof!
longer (non-golf) version
Same functions, same syntax; added only breaks and brackets
f <- function(n) {
  b <- "bottles of beer"
  p <- paste0
  w <- "on the wall"
  cat(n, b, p(w, ","), n, p(b, "."), "\n")

  if (n > 1) {
    cat("Take one down and pass it around,", n - 1, b, p(w, ".\n"))
    f(n - 1)
  } else {
    cat("Go to the store and buy some more, 99", b, p(w, "."))
  }
}

f(99)

It writes 1 bottles of beer during the last two rounds.

PHP, 256 bytes
<?php $b=' bottle';$o=' of beer';$t=' on the wall';$i=99;while($i>=1){$s=$i!=1?'s':'';echo "$i$b$s$o$t, $i$b$s$o.\n".($s?"Take one down and pass it around":"Go to the store and buy some more").", ".($i==1&&--$i>-3?99:--$i)."$b".($i!=1?'s':'')."$o$t.\n\n";}

Java, 282 bytes
First time I've ever done this "code golf" thing. Sorry if I get this wrong or something. Please let me know as a comment if there is something I could improve or if I ruined the format or something. Thanks!
public class P{public static void main(String a[]){int n=99;while(n>0){System.out.println(n+" bottles of beer on the wall, "+n+" bottles of beer. \n Take one down and pass it around, "+(n--)+" bottles of beer on the wall.");}System.out.println("Go to the store and buy some more!");}}

Unsimplified: (337 Bytes)
    public class P {
    public static void main(String a[]) {
        int n = 99;
        while (n > 0) {
            System.out.println(n + " bottles of beer on the wall, " + n + " bottles of beer. \n Take one down and pass it around, " + n-- + " bottles of beer on the wall.");
        }
        System.out.println("Go to the store and buy some more!");
    }
}

Welcome to PPCG! You could save a lot of bytes by removing unnecessary spaces, such as those in P {, n = 99, n > 0, ) {, " + n + ", etc.

@ETHproductions oh okay great, didn't see those. Thank you! I love the concept of this site, can't wait to do more "code golfs".

You can also use a instead of args and --n instead of (n=n-1). Also the last line should be Go to the store and buy some more.

@intrepidcoder ok great tips thanks. Woops, missed that last part, will add it in.

Can you remove the parentheses around n--? It would look much cooler without the parentheses!

@anatolyg lol will do! I agree, it will look much cooler, and reduce my byte count.

It's been a while, but I just noticed that your output doesn't match the challenge requirements. (Specifically, the last few lines have some problems, and the whitespace isn't right.) So I've flagged this answer for deletion; but if you see this, please feel free to edit it to produce the right output so it can be undeleted. :)

Emacs Lisp, 290 Bytes
(defun b (n) (format "%d bottle%s of beer" n (if (eq n 1) "" "s")))
(defun bw (n) (concat (b n) " on the wall"))
(defun l (n) (concat (bw n) ", " (b n) ".\nTake one down, pass it around, " (bw (- n 1)) ".\n" (if (> n 1) (l (- n 1)) "Go to the store and buy some more.\n")))
(message (l 99))

You forgot to “go to the store and buy some more”.

The first line would be shorter as (defun b (n) (format "%d bottle%s of beer" n (if (> n 1) "s" "")))

@manatwork Thanks for the help. Updated it.

Perl 6, 213 bytes
my$b=' bottle';my$e=' of beer';my$o=" on the wall";for 99…1 {my$c=$_~$b~'s'x?($_-1)~$e;say "Take one down and pass it around, $c$o.\n"if $++;say "$c$o, $c."};say "Go to the store and buy some more, 99{$b}s$e$o."

my$b=' bottle';
my$e=' of beer';
my$o=" on the wall";
for 99…1 {
  my$c=$_~$b~'s'x?($_-1)~$e;
  say "Take one down and pass it around, $c$o.\n"if $++;
  say "$c$o, $c."
};
say "Go to the store and buy some more, 99{$b}s$e$o."

Mouse-2002, 275 bytes
Mouse hates strings. Actually, it doesn't even know anything about them, because the closest thing to a string you can put on the stack is a char. Anything between " " is printed, but you can't put strings in variables or manipulate them.1
99I:(I.0>^I.1=["1 bottle of beer on the wall, 1 bottle of beer.!Go to the store and buy some more, 99 bottles of beer on the wall.!"|I.!" bottles of beer on the wall, "I.!" bottles of beer.!Take one down and pass it around, "I.1-!" bottles of beer on the wall.!!"]I.1-I:)$

That's shamefully long, for a stack-based language.
Explained:
99I:     ~ i = 99
(        ~ while(true)
  I.0>^  ~ if i > 0 == false then break
  I.1=[  ~ if i == 1 print this string
    "1 bottle of beer on the wall, 1 bottle of beer.!Go to the store and buy some more, 99 bottles of beer on the wall.!"
  | I.!" bottles of beer on the wall, "I.!" bottles of beer.!Take one down and pass it around, "I.1-!" bottles of beer on the wall.!!" ~ else print this interpolated one
  ] ~ end if
  I.1-I: ~ i = i-1
) ~ end while
$ ~ end prog

1That's why I'm planning to reimplement and update Mouse into newmouse!

Milky Way 1.0.2, 255 bytes
99&{~:?{1b_" bottle"_" bottles"}" of beer"+;:>;+:" on the wall, "+;".\n"++"Take one down and pass it around, "<1-:>++<?{1b_" bottle"_" bottles"};>+" of beer on the wall."+<?{_""_"\rGo to the store and buy some more, 99 bottles of beer on the wall."};>+!<}

It's really too long to explain in detail here, but check the docs for info.

Usage
./mw <path-to-code>

Perl 5, 222 bytes
Ungolfed:
$b=" bottles of beer";
$B=" bottle of beer";
$w=" on the wall";
for($i=99;$i>1;$i--){
print"$i$b$w, $i$b.\nTake one down and pass it around, ".($i-1)."$b$w.\n\n";
}
print"1$B$w, 1$B.\nGo to the store and buy some more, 99$b$w.";

Golfed:
$b=" bottles of beer";$B=" bottle of beer";$w=" on the wall";for($i=99;$i>1;$i--){print"$i$b$w, $i$b.\nTake one down and pass it around, ".($i-1)."$b$w.\n\n";}print"1$B$w, 1$B.\nGo to the store and buy some more, 99$b$w.";

I'm convinced there has to be a way to improve this approach with some sort of whitespace collapsing. Is there a special variable in Perl that is printed after alphanumeric characters, that you can set? I wonder...
Either way, I feel pretty content for a 5-minute golf.

Lua, 267 263 bytes
q=" bottle"r=q.."s"s=" of beer"t=" on the wall"u="\nTake one down and pass it around, "v="\nGo to the store and buy some more, "w=r..s for n=99,1,-1 do io.write(n,w,t,", ",n,w,".",n>1 and u..n-1 ..(n>2 and w or q..s)or v.."99"..r..s,t,".\n\n")w=(n>2 and w or q..s)end

I didn't see lua under the answers yet, only golflua, correct me if im wrong.
Explatation:
q=" bottle"     -- assigning parts of the text to variables in as
r=q.."s"        -- long pices as it's practical
s=" of beer"
t=" on the wall"
u="\nTake one down and pass it around, "
v="\nGo to the store and buy some more, "
w=r..s          -- bracause r..s/r,s repeats 4 times its shorter to assign it to w

for n=99,1,-1 do
    io.write(n,w,t,", ",n,w,".",    --putting it together

        n>1 and u..n-1              -- "take one down..." or "go to the store..."
            ..(n>2 and w or q..s)   -- "bottels or bottle..."
        or v.."99"..r..s,t,".\n\n"  -- "n-1" or "99"
    )

    w=(n>2 and w or q..s)           -- this condition repeats 2 times, it changes "bottles" to "bottle"
end

Tryout link: Execute Lua Online.
Type lua in the comandline and then dofile("99bob.lua").
Testrun:
3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.

2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.

1 bottle of beer on the wall, 1 bottle of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.

I tried to write the program golfed from the start, that probably wasn't a good idia.
Edit: thanks to manatwork for r=q.."s".

Even if a Lua solution would already exist, you are welcome to add another one if the algorithm is significantly different. r=" bottles" → r=q.."s".

JavaScript ES6, 298 bytes
for(var a=[" bottles of beer","on the wall","take one down and pass it around","go to the store and buy some more",99];0<a[4];)a[0]=1===a[4]?a[0].replace("s",""):a[0],console.log(a[4]+a[0]+" "+a[1]+", "+a[4]+a[0]+". "+(1===a[4]?a[3]:a[2])+", "+(0===a[4]-1?"99"+a[0]+"s":a[4]+a[0])+" "+a[1]),a[4]--;

SELECT., 7313057 bytes
This is not a SELECT. program that prints the 99 bottles lyrics. It is a link to a pastebin of a python program that prints a SELECT. program that prints the 99 bottles lyrics. The latter program is over 7 megabytes long, and unsuitable for posting here.
http://pastebin.ca/3372405
The program that generated this program looks like this:
######GENERATION CODE######
init(185,49,1)
makenum(10)
square()
dec()
dec()
var("99")
loop("main",computei=True,savelist=["99"])
var("i")
go(1)
makeneg1()
multiply(-1)
go(1)
fetch("99")
add(-1)
var("num")
go(1)
makeone()
add(-1)
var("curnum")
go(1)
makenum(10)
go(-1)
intdiv()
go(-5)
var("mod")
go(5)
var("div")
ifnonpositive("tens")
els("tens",True)
digitprintXY(0,0)
endif("tens")
go(1)
fetch("mod")
var("mod")
digitprintXY(6,0)
go(1)
drawstringliteral("bottle",16,0)
go(1)
fetch("curnum")
go(1)
makeneg1()
add(-1)
var("onetest")
ifzero("plural")
go(1)
drawstringliteral("of beer on the wall,",51,0)
els("plural")
go(1)
drawstringliteral("s of beer on the wall,",46,0)
endif("plural")
go(1)
fetch("div")
ifnonpositive("tens")
els("tens",True)
digitprintXY(0,13)
endif("tens")
go(1)
fetch("mod")
digitprintXY(6,13)
go(1)
drawstringliteral("bottle",16,13)
go(1)
fetch("onetest")
ifzero("plural")
go(1)
drawstringliteral("of beer.",51,13)
els("plural")
go(1)
drawstringliteral("s of beer.",46,13)
endif("plural")
go(1)
drawstringliteral("Take one down and pass it around,",0,26)
go(1)
fetch("num")
ifzero("nomore")
go(1)
drawstringliteral("No more bottles ofbeer on the wall.",0,39)
els("nomore",True)
go(1)
makenum(10)
go(-1)
intdiv()
go(-5)
var("mod")
go(5)
ifnonpositive("tens")
els("tens",True)
digitprintXY(0,39)
endif("tens")
go(1)
fetch("mod")
digitprintXY(6,39)
go(1)
drawstringliteral("bottle",16,39)
go(1)
fetch("num")
go(1)
makeneg1()
add(-1)
ifzero("plural")
go(1)
drawstringliteral("of beer on the wall.",51,39)
els("plural")
go(1)
drawstringliteral("s of beer on the wall.",46,39)
endif("plural")
input()
clear()
endif("nomore")
go(1)
endloop("main",savelist=["99"])
go(1)
drawstringliteral("No more bottles of beer on the wall,",0,0)
go(1)
drawstringliteral("no morebottles of beer.",0,13)
go(1)
drawstringliteral("Go to thestore and buy some more,",0,26)
go(1)
drawstringliteral("99 bottles of beeron the wall.",0,39)
writetofile("99bottles.sel")

C, 256 bytes
Nice round number.
*a=" bottles of beer",*b=" on the wall",*c="Take one down and pass it around, ",d=100;main(){while(--d){printf("%d%s%s, %d%s, \n",d,a,b,d,a);d-1&&printf("%s%d%s%s. \n",c,d,a,b);}puts("Go to the store and buy some more, 99 bottles of beer on the wall.");}

With whitespace:
*a=" bottles of beer",
*b=" on the wall",
*c="Take one down and pass it around, ",
d=100;main(){
    while(--d){
        printf("%d%s%s, %d%s, \n",d,a,b,d,a);
        d-1&&printf("%s%d%s%s. \n",c,d,a,b);
    }
    puts("Go to the store and buy some more, 99 bottles of beer on the wall.");
}

Fun fact: I accidentally posted this on stack exchange and then realised a few minutes later that it was on the wrong post. Ooops.

Jolf, 193 bytes
Try it here! I can still probably golf a byte or two...
Ώ³γhHΖp{" Ξ\x000k"γΆΆγ"% of beer on the wall, "ζγ"% of ΞΓi
"ζ?wγΆ"Ξ    ψone Ξp«Ξit ΞpDΖ"wγ"% of ΞΙon the Ξy
"p{" Ξ\x000k"wγ"Go to the Ξrand buy ΞzδΞB\x00Ε 99 Ξώof ΞΙon the Ξy"1Μw_z~1dΏH

Here's the main concept: this defines a function Ώ that returns the bottle line of an input, calls it 1 for a default, and:
Μw_z~1dΏH
Μ           maps
 w_z~1      0..99 reversed
      dΏH   with that function

Ugh that hurt my head.

XSLT 1.0 (no EXSLT), 845 bytes
<transform xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0"><output method="text"/><template match="/"><call-template name="b"/></template><template name="b"><param name="n" select="99"/><if test="$n>0"><variable name="o"><if test="$n>1"><value-of select="$n -1"/></if><if test="$n=1">99</if></variable><variable name="p"><if test="$n>1">s</if></variable><variable name="q"><if test="$n!=2">s</if></variable><variable name="z"><if test="$n=1">Go to the store and buy some more</if><if test="$n>1">Take one down and pass it around</if></variable><variable name="w" select="concat($n,' bottle',$p,' of beer')"/><value-of select="concat($w,' on the wall, ',$w,'.
',$z,', ',number($o),' bottle',$q,' of beer on the wall.
')"/><call-template name="b"><with-param name="n" select="$n -1"/></call-template></if></template></transform>

With a bit less golf in it:
<transform xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <output method="text"/>
    <template match="/">
        <call-template name="b"/>
    </template>
    <template name="b">
        <param name="n" select="99"/>
        <if test="$n>0">
            <variable name="o">
                <if test="$n>1">
                    <value-of select="$n -1"/>
                </if>
                <if test="$n=1">99</if>
            </variable>
            <variable name="p">
                <if test="$n>1">s</if>
            </variable>
            <variable name="q">
                <if test="$n!=2">s</if>
            </variable>
            <variable name="z">
                <if test="$n=1">Go to the store and buy some more</if>
                <if test="$n>1">Take one down and pass it around</if>
            </variable>
            <variable name="w" select="concat($n,' bottle',$p,' of beer')"/>
            <value-of select="concat($w,' on the wall, ',$w,'.
',$z,', ',number($o),' bottle',$q,' of beer on the wall.
')"/>
            <call-template name="b">
                <with-param name="n" select="$n -1"/>
            </call-template>
        </if>
    </template>
</transform>

Notes
Text compression
The use of variables as abbreviations isn't as extensive as in other languages; for example, note the repetition of ' bottle' as a constant. In XSLT, the binding of an XPath expression or some arbitrary text to a variable name requires at least 29 characters of overhead, excluding the name and content itself:
<variable name="name" select="expr"/>
<variable name="name">text</variable>

Say, for example, that one would like to replace all instances of the XPath string literal ' bottle' (8 chars) with $b (2 chars). Each substitution saves 6 characters. However, given the definition <variable name="b"> bottle</variable> (38 chars), there is no net profit unless there are 7 or more such sequences to replace.
The use of the <variable/> element is limited to evaluating conditions. The version of XPath used with XSLT 1.0 does not have its own if/else constructs, but XSLT itself does, and <variable/> is necessary to retrieve the results in a useful form.
$n -1
The expression $n -1 appears twice. The space after the $n is required since - is a valid name character in XPath.
Inequalities
Wherever practical, any instance where != originally appeared was changed to use > instead. If the program counted down to 0 instead of resetting to 99 after 1, the plural check for $n would be incorrect at 0.
Tail recursion
The template named b recurses, counting the variable n down by one each time. XSLT provides looping constructs, but they are for traversing structures found in the input document. A task like this one would be substantially easier if we had an input doc containing the countdown (<doc><value>99</value><value>98</value><value>97</value>...</doc>) but at that point we're cheating (or we at least have to count the input doc in our byte count).
Run
Run using xsltproc:
xsltproc 99.xsl 99.xsl

Yes, 99.xsl is passed twice: Once as the XSLT document and then as the XML document to be transformed. An input document must be present because an XSLT processor generally requires one to begin running. (XSLT is designed to define a transformation from an input document to an output document; running a transform solely with command-line parameters as I've done here is atypical.) For this program, any well-formed XML document will suffice as input, and, XSLT being an application of XML, any well-formed XSLT transform is by definition a well-formed XML document.

Bad memories. At least it's good from something.

C#, 267 bytes
()=>{for(int x=99;x>0;){string e="",s="s",t=x>1?s:e,b=" bottle",o=" of beer",w=$"{o} on the wall",a=x+b+t;System.Console.Write($"{a+w}, {a+o}.\n"+(x-->1?$"Take one down and pass it around, {x+b+(x>1?s:e)+w}.\n\n":$"Go to the store and buy some more, 99{b}s{w}."));}};

C# lambda where the output is stdout and without output. You can try it on .NetFiddle.
Code :
()=>{
    for (int x=99;x>0;) {
        string e="",s="s",t=x>1?s:e,b=" bottle",o=" of beer",w=$"{o} on the wall",a=x+b+t;
        System.Console.Write($"{a+w}, {a+o}.\n"+(x-->1
            ?$"Take one down and pass it around, {x+b+(x>1?s:e)+w}.\n\n"
            :$"Go to the store and buy some more, 99{b}s{w}."));
}};

Kudos to Igby Largeman for his solution. I think it is better than mine (shorter if it had use a lambda too).

Common Lisp (Lispworks), 431 bytes
(defun f()(do((i 99(- i 1)))((= i 2))(format t"~S bottles of beer on the wall, ~S bottles of beer.~%Take one down and pass it around, ~S bottles of beer on the wall.~%~%" i i(- i 1)))(format t"2 bottles of beer on the wall, 2 bottles of beer.~%Take one down and pass it around, 1 bottle of beer on the wall.~%~%1 bottle of beer on the wall, 1 bottle of beer.~%Go to the store and buy some more, 99 bottles of beer on the wall.~%"))

Ungolfed:
(defun 99-Bottles-of-Beer ()
  (do ((i 99 (- i 1))) ((= i 2))
    (format t "~S bottles of beer on the wall, ~S bottles of beer.~%Take one down and pass it around, ~S bottles of beer on the wall.~%~%" i i (- i 1)))
  (format t "2 bottles of beer on the wall, 2 bottles of beer.~%Take one down and pass it around, 1 bottle of beer on the wall.~%~%1 bottle of beer on the wall, 1 bottle of beer.~%Go to the store and buy some more, 99 bottles of beer on the wall.~%"))

Maple, 233 bytes
Added (unnecessary) newlines for code readability:
seq(printf("%d %s of beer on the wall,\n%d %s of beer.\nTake one down, pass it around,\n%d %s of beer on the wall.\n\n",
         i,`if`(i<>1,"bottles","bottle"),
         i,`if`(i<>1,"bottles","bottle"),
         i-1,`if`(i-1<>1,"bottles","bottle")),
i=99..1,-1);

R, 138 225 bytes
    a='bottles of beer on the';for(i in 99:1)cat("",i,a,"wall,",i,"bottles of beer.\n","Take one down and pass it around,",i-1,a,"wall.\n\n")
a='bottles of beer on the'
for(i in 99:2)
    cat("",i,a,"wall,",i,"bottles of beer.\n","Take one down and pass it around,",i-1,a,"wall.\n\n")
cat(1,a,"wall,","1 bottle of beer.\nGo to the store and buy some more,",99,a,"wall.")

EDIT-- missed the change in the last verse.

There's still a little bit to fix: it prints 1 bottles (<- with s) in the end of the second last verse and at the beginning of the last verse.

dc, 269 237 bytes
299si[lid1-dsi3/li3>vdn[ bottle]P1<n]sm[[s]P]sn[ of beer]so[ on the wall]sp[, ]sq46sr10ss[[Take one down and pass it around]Pq]st[d4<t[Go to the store and buy some more]P]su[99+]sv[lmxloPlpPlqPlmxloPlrPlsPluxlqPlmxloPlpPlrPlsdPP3<z]dszx

Shaved off about 30 bytes by replacing two counters w/ 1 299/3 counter; couple of unnecessary variable loads replaced w/ stack duplications; code point 46 instead of [.].

tcl, 243
proc B {} {set x " bottle[expr $::i>1?{s}:{}] of beer"}
set i 99
time {puts "$i[B][set w " on the wall"], $i[B].
Take one down and pass it around, [incr i -1][B]$w."} 98
puts "1[B]$w, 1[B].
Go to the store and buy some more, [incr i 98][B]$w."

demo

tcl, 255
proc B i {set x " bottle[expr $i>1?{s}:{}] of beer"}
set w " on the wall"
set i 99
while \$i>1 {puts "$i[B $i]$w, $i[B $i].\nTake one down and pass it around, [incr i -1][B $i]$w."}
puts "1[B $i]$w, 1[B $i].\nGo to the store and buy some more, 99[B 9]$w."

demo

Ohm, 166 164 131 bytes (CP437)
Ohm has been updated a lot since I first posted this! Now I'm using some new features.
98@R≥î:_Θ▀a▀_Ωü▀5≡axÿx}iöÜª├┬ï°∙╟╕Ö√²┬zτα█▀_≤îΘ..ÑÑ;1Θ▀a▀1Ω▀â"190└δ*6²H"╥╨;√⌡ÄwîFδh#|a≡Ω█≡5▓Θ╕Ç≈└hÑqûI▀J
D≤y.sa·▀╖+╫Ä▀a
Θ▀Ö$k·}Qy╦▀

Try it online!
Explanation
The main wire contains a lot of compressed strings, which are delimited by ▀.
Main wire:

98@R    Push the range 98...1.
≥î      Increment each.
:       Map each number n:
 _Θ      Call the second helper wire on n.
 ▀a▀     Push ", ".
 _Ω      Call the first helper wire on n.
 ü       Push a space.
 ▀...▀   Push "of beer.\nTake one down and pass it around, "
 _≤îΘ    Call the second helper wire on n-1.
 ..ÑÑ    Push a period and two newlines.
;       (end map)
1Θ      Call the second helper wire on 1.
▀a▀     Push ", "
1Ω      Call the first helper wire on 1.
▀...▀   Push " of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall."
J       Join the entire stack together. Implicitly output.

First helper wire:

D       Duplicate TOS.
≤       Decrement.
y       Get sign (0 or 1).
.sa·    Repeat 's' that many times.
▀╖+╫Ä▀  Push " bottle"
a       Swap top two elements.

Second helper wire:

Θ           Call the first helper wire.
▀Ö$k·}Qy╦▀  Push " of beer on the wall"

Windows Batch, 279 bytes
some optimization later
@echo off
set A=99
set B= bottle
set C=s of beer
set D= on the wall
:l
echo %A%%B%%C%%D%, %A%%B%%C%.
set/a A-=1
if %A%==1 set C=%C:~1%
if %A%==0 (echo Go to the store and buy some more, 99%B%s%C%%D%.
exit)else (echo Take one down and pass it around, %A%%B%%C%%D%.
echo.)
goto l

(my first post here, code actually from 2016)

Works, but you're not handling dropping the 's' when the bottles are singular

@Chirishman if %A%==1 set C=%C:~1% drops the s when bottles are singular

Ly, 272 bytes
(99)1[psu" bottles of beer on the wall, "&olu" bottles of beer.\n"&ol1-sp"Take one down and pass it around, "&olu" bottles of beer on the wall.\n"&ol1G]pu" bottle of beer on the wall, 1 bottle of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall."&o

Try it online!

PowerShell (204 bytes)
$b='bottles of beer';$w="on the wall";$p="Take one down and pass it around";99..2|%{"$_ $b $w, $_ $b.`n$p, $($_-1) $w.`n"};$b=$b.remove(6,1);"1 $b $w, 1 $b.`nGo to the store and buy some more, 99 $b $w."

Edit (now 198 bytes as per @BradC comments, thx buddy!):
$b=' bottles of beer';$w=" on the wall";$p="Take one down and pass it around";99..2|%{"$_$b $w, $_$b.`n$p, $($_-1)$w.`n"};$b=$b.remove(6,1);"1$b$w, 1$b.`nGo to the store and buy some more, 99$b$w."

Nice. I think you can save a couple of bytes by including a space at the beginning of both @b and @w.

Python, 172 bytes
a="\n%s bottles of beer on the wall"
t="\n\nTGaok et oo nteh ed oswtno raen da npda sbsu yi ts oamreo umnodr e"
d=99
while d:print(a%d*2)[:-12]+t[d==1::2]+a%(d-1or 99);d-=1

Try it online!
Explanation:
a="\n%s bottles of beer on the wall"

Stores the first line of the lyrics with a formatted argument.
t="\n\nTGaok et oo nteh ed oswtno raen da npda sbsu yi ts oamreo umnodr e"

Stores the third line. The characters at even indices make up Take one down and pass it around, and the characters at odd indices make up Go to the store and buy some more.
d=99

Counter variable for the current number of bottles, initialized to 99.
while d:                                           d-=1

In Python (and most other languages), a non-zero integer is truthy, while 0 is falsey. This while loop (although kind of hacky) repeats until d is 0, subtracting 1 from d every time.
        print             +          +            ;

I used only one print statement to save bytes, and just added multiple strings together.
             (a%d*2)[:-12]

Formats the first line with the number of bottles, repeats it, and chops off the on the wall part from the first line to make the second line.
                           t[d==1::2]

Extended slicing. Takes every other character, starting from the first character if d is not 1, but starting from the second character is d is 1. In Python, booleans can be implicitly converted to integers, so true would turn into 1, and false would turn into 0.
                                      a%(d-1or 99)

Just the first line again, except this time with one less bottle. Again, anything except 0 is truthy, so if d-1 is 0, it will give 99 instead.

Appears not to handle plural for n=1 (1 bottle of beer on the wall, 1 bottle of beer.) properly.

Python 3, 243 bytes
b=lambda x,y: f'{x} bottle{("","s")[x>1]} of beer{(""," on the wall")[y]}'
p=print
for i in range(99,0,-1):
 p(f'{b(i,1)}, {b(i,0)}.')
 p((f'Go to the store and buy some more, {b(99,1)}.',f'Take one down and pass it around, {b(i-1,1)}.')[i>1])

Try it online!

You should include the code in your answer. TIO has a nifty auto-formatter for PPCG if you click the link icon at the top of the page

225 bytes

Hah - yeah, this is tougher than it looks :)

Stax, 82 bytes
äÇEà╧<à■≤ÄâDφl%H/╢S▌╙♂Å5ε─U¥ΦioJ╦D╖↨{)X╗√╛ⁿ?fJ∞L^╧êτ╝♪p1j■GTq⌂ÿ█♀├=δMséaΦ≡&NQ▌←²╠↕

Run and debug it

Powershell, 208 207 bytes
-1 byte, thanks @Veskah
inspired by Veskah's answer.
$l,$e,$o=" bottles of beer"," bottle of beer"," on the wall"
99..1|%{"$_$l$o, $_$l."
if($_---le2){$l,$e=$e,$l}"$(("Take one down and pass it around, $_","Go to the store and buy some more, 99")[!$_])$l$o.
"}

Psssh, is a swap a clever stuff? :)

It's about 19 bytes more clever than mine.

Make that 12, you reminded me that strings can be multi-lined for free

Looks like you can save a byte by doing if(){}"stuff". Seems if blocks don't need a newline or semi-colon after to work.

Python 3, 288 266 253 (credit: Jo King) 251 242 bytes
b=lambda x:"%s bottle%s of beer"%(x,"s"*(x>1));o="on the wall"
for i in range(99,0,-1):print("\n%s %s, %s.\n%s"%(b(i),o,b(i),["Take one down and pass it around, %s %s."%(b(i-1),o),"Go to the store and buy some more, %s %s."%(b(99),o)][i==1]))

There is already a shorter Python 3 answer :P

it doesn't work in python 3, and fails with a TypeError: 'NoneType' object is not subscriptable

223

210 bytes

except " instead of ' in ' on the wall' for consistency :P

Keg, 222 bytes
c&(c|&:.& bottle&:&1>[s] of beer on the wall\, ^(,)&:.& bottle&:&1>[s] of beer\.
^(,)&:&1=[Go to the store and buy some more|Take one down\, pass it around]\, ^(,)&:&1=[\9\9|&;:.&] bottle&:&1>[s] of beer on the wall\.

^(,

Try it online!
Explanation

First it stores the current counter into the accumulator
Then it prints all verses accordingly until the last verse
Finally this hard-codes the final verse

Let's see if I can get this any shorter.

Consider adding a short explanation to your answer. Code-only answers may be automatically flagged as low-quality. See other answers for examples.

This can easily be shortened using the register commands. But I'm on mobile right now, so I'll probably give a shortened version later. About 12 bytes can be golfed.

Whitespace, 1977 1342 1013 877 bytes
(very much whitespace)
Try it online!
Finally, under 1000 bytes.
-635 bytes by manually correcting the automatic whitelips labels
-329 bytes by offsetting the letters and using 0 for spaces
-136 bytes by pushing whole sentences in one big integer and using divmod to calculate the letters.
visible code:
sssttsssttl push 99

lssssl      label main:
lsttl       call wall
lststl      call comma_space
lsttsl      call beer
lstsl       call stop_nl
ssstl       push 1
tsst        sub
lstttl      call take
lststl      call comma_space
lsttl       call wall
lstsl       call stop_nl
ssststsl    push newline (10)
tlss        printc
lslssl      jump main

lssttl      label take:
sls         dup
ltssssl     jz store
ssstststssl "T"
tlss        printc
ssstsstsstttssttsssttsststtstststststssststssttsttttttstsssstttstsssttstttssstssssttsssssssssttsstttststttsssttsssttststtstsssttttstssssttsstssssttl
            "ake one down and pass it around"
lsll        jmp print

lsssssl     label store:
sll         drop
sssttsssttl push 99
ssstssstttl "G"
tlss        printc
ssstsstststttttsttssssststtstttttssttsstsststttssssttsssttsssssttstssttstststtsttsttstssssstsssttstsstttttttssssttsttsttstststsststtssttsstssttttsttstttl
            "o to the store and buy some more"
lstl        call print
lststl      call comma_space
lsttl       call wall
lstsl       call stop_nl
lll         end

lsssl       label stop_nl:
ssststttsl  "."
tlss        printc
ssststsl    <lf>
tlss        printc
ltl         return

lssstl      label comma_space:
ssststtssl  ","
tlss        printc
sssl        0
lsll        jmp print

lsstl       label wall:
lsttsl      call beer
ssststssststssssttttttsstttsstststttstssttsstsssssstttttttsl
            " on the wall"
lsll        jmp print

lsstsl      label beer:
lststtl     call bottles
ssstssstssssstsssttsttsttstttssstttsttttsl
            " of beer"
lsll        jmp print

lsssttl     label bottles:
sls         dup bottlecount
tlst        printi
sssttsststsstssssstttsttssttttssssl
            " bottle"
lstl        call print
sls         dup bottlecount
ssstl       push 1
tsst        sub
ltststl     jz return
ssstssttl   's'

lssl        label print:
sssttstsl   26
stsstl      copy 1
stsstl      copy 1
tstt        mod
sls         dup
ltstssl     jz space
ssstssssssl push 64
tsss        add
lsstssl     label space:
ssstsssssl  push 32
tsss        add
tlss        printc
tsts        div
sls         dup
ltsstsl     jz end_print
lsll        jump print
lssstsl     label end_print:
sll         drop
lsststl     label return:
ltl         return

Unix TMG, 231 byte
o:parse(v)[n<143?]\o;v:b l b={3< on the wall, >3<.>*2 1< on the wall.>**};b:decimal(n)([n>1?]={<s>}|={})={2< bottle>1< of beer>};l:[--n?]={<Take one down and pass it around, >}|[n=143]={<Go to the store and buy some more, >};n:143;

Expanded:
o: parse(v) [n<143?]\o;
v: b l b = {3 < on the wall, > 3 <.> * 2 1 < on the wall.> * * };
b: decimal(n) ([n>1?]={<s>}|={}) = {2 < bottle> 1 < of beer>};
l: [--n?] = {<Take one down and pass it around, >}
 | [n=143] = {<Go to the store and buy some more, >};
n: 143;

The fact that this 1972 language uses octal instead of decimal doesn't help in this challenge

If it can't be written without the newlines then the newlines should be counted.

This seems to be 235 bytes, not 231

@JoKing, deleted the newlines. Thanks, it works without them.

Python, 357 339 310 bytes
After shaving off the fat to meet the expected output it didn't save as many bytes as I had expected:
def B(n):A='bottle';return A if n==1 else A+'s'
A=99
while A>=1:F='of';D=F+' beer';E='more';C=D+' on the wall';G='no '+E;H=f"Take one down and pass it around, {A-1 if A>1 else G} {B(A-1)} {C}\n"if A!=1 else f"Go to the store and buy some {E}, 99 {B(2)} {C}.";print(f"{A} {B(A)} {C}, {A} {B(A)} {D}.\n"+H);A=A-1

The following code is for code-golf output:
This is the lowest I can hit so far
def B(n):A='bottle';return A if n==1 else A+'s'
A=99
while A>=0:G='of';D=G+' beer';E='more';C=D+' on the wall';F='no '+E;print(f"{A} {B(A)} {C}, {A} {B(A)} {D}.\nTake one down and pass it around, {A-1 if A>1 else F} {B(A-1)} {C}.\n"if A!=0 else f"No {E} {B(2)} {C}, {F} {B(2)} {D}.\nGo to the store and buy some {E}, 99 {B(2)} {C}.");A=A-1

Here's the sort of bigger version:
def get_bottles(num_bottle):
    p = "bottle"

    return p if num_bottle == 1 else p + "s"

bottleNo = 99

while bottleNo >= 0:
    of = "of"
    beer = of + " beer"
    more = "more"
    botw = beer + " on the wall"
    noMore = "no " + more

    print(f"{bottleNo} {get_bottles(bottleNo)} {botw}, {bottleNo} {get_bottles(bottleNo)} {beer}.\nTake one down and "
          f"pass it around, {bottleNo - 1 if bottleNo > 1 else noMore} {get_bottles(bottleNo - 1)} {botw}.\n" if
          bottleNo != 0
          else f"No {more} {get_bottles(2)} {botw}, {noMore} {get_bottles(2)} {beer}.\nGo to the store "
               f"and buy some {more}, 99 {get_bottles(2)} {botw}.")

    bottleNo = bottleNo - 1

You went through the trouble of making all your variables into one character, but forgot num_bottle. If you do that there as well, you could shave off a few bytes

Good catch, that saved me 18 chars. Thanks!

Plain TeX (237 characters)
\input tikz\let~\def~\1#1{ bottle#1 of beer}~\7{~ on the wall}~\9#1#2{\def~{ #1\1#2}Take one down and pass it around,\7.\bigskip\par\7,~.\par}~~{ 99\1s}\7,~.\par\foreach\2in{98,...,2}{\9\2s}{\91 }Go to the store and buy some more,\7.\bye

Try the Code on Overleaf

Any Comments Appreciated
Please help me golf this code to an unhealthy extent.

@AdHocGarfHunter Would you want to accept a challenge? Do you want to find out whether you can minify this further down?

AWK, 198 bytes
a=100{d=" on the wall";e=" of beer";for(f=" bottle";a--;$0=g)g=g (a?(c=a f (a-1?"s":b)e)d", "c".\nTake one down and pass it around, "c d".\n\n":"Go to the store and buy some more, 99"f"s"e d".\n")}1

Try it online!
I didn't see AWK on the last yet, so here's one example.
This code will print the whole "99 bottles..." dialogue for every line of input fed into the code. I'm not sure how best to break this apart, but maybe this helps?
a=100{ ... }
Something truthy to trigger the code block, also sets the loop variable a to 100.
d=" on the wall";e=" of beer";
Store some re-used strings in variables.
for(f=" bottle";a--;$0=g)
The loop control, first set f in the loop init to save a ;.  Then stop/go based on a after decrementing.  At the end of the each iteration, set $0 to the accumulated output in g.
g=g ( ... )
Append the text for the next number of bottes to the accumulating output.  Sadly the space is needed to avoid AWK parsing g( as a function call.
a?
Ternary for "is there more bottles?"
(c=a f (a-1?"s":b)e)
When there is more than one bottle... Generate "NN bottle of beer" or "NN bottles of beer" to be appended to g and also store that string in c.  Using b in the ternary instead of "" saves a character and works since using an uninitialized variables as a string in AWK are treated like empty strings.
d", "c".\nTake one down and pass it around, "c d".\n\n"
Also append the rest of the text we need, making use of c set previously
:"Go to the store and buy some more, 99"f e d".\n"
When at zero bottles, append the last bit of text.
1
A truthy value with no code block causes the default action, which is to print the value of $0

 <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b">

Shortest Solution by Language

Language	User	Score

Leaderboard

	Author	Language	Size

{{PLACE}}	{{NAME}}	{{LANGUAGE}}	{{SIZE}}	Link

{{LANGUAGE}}	{{NAME}}	{{SIZE}}	Link

Pxem, Filename: 131 bytes + Content: 61 bytes = 192 bytes.
I escaped spaces too (\040).

Filename (escaped): c.z.e,.o\040.oq.e.s..o\n.o\001.-.c\001.y.sGo\040to\040the\040store\040and\040buy\040some\040more,\040.pc.\n.e.d.a.tTake\040one\040down\040and\040pass\040it\040around,\040.p.m.e..o\n.o\n.o.a
Content (escaped): .cd.y.t\040on\040the\040wall.mXXq.a.s.c.n.t\040of\040beer.m\001.zXXs.a\040bottle.p

Actual source
Filename
c.z.e,.o .oq.e.s..o
.o.-.c.y.sGo to the store and buy some more, .pc.
.e.d.a.tTake one down and pass it around, .p.m.e..o
.o
.o.a

Content
.cd.y.t on the wall.mXXq.a.s.c.n.t of beer.m.zXXs.a bottle.p

With comments
Inspired by Dorian's answer in Whitespace at first, then golfed well. Though both Whitespace and Pxem are stack-oriented programming language, their features are different, as in these:

Whitespace has label and (conditional) go-to commands; Pxem has while-done loop.
Both of them have subroutine, but Pxem can have only one subroutine. Also that of Pxem works as a function rather than a subroutine.
Whitespace has commands that accesses to non-top items of its stack; Pxem can reverse items on stack.
And more, I think.

Overall of the program

Iteration from 99 to 1 on filename.
Content stands for a subroutine to output strings that are frequently recycled.
  - It depends on top of stack. If the item is more than 99, it outputs " on the wall" in addition to "[0-9]+ bottles? of beer".

Filename
XX.z
# push 99;
# while size<2||[ $(pop) -ne $(pop) ]; do
.ac.zXX.z
  # for i in $(reverse $(call $stack)); do
  #   push $i; done
  ## NOTE let it be "call" next time
  .a.eXX.z
  # push 44; putc $(pop); push 32; putc $(pop)
  ## NOTE DOTo is actually:
  ## if !empty; then : do above; fi
  .a,.o .oXX.z
  # push 113; call; pop
  ## NOTE 113>=100
  .aq.e.sXX
  # push 46; putchar $(pop);
  # push 10; putchar $(pop)
  .a..o\n.oXX.z
  # push 1;
  # push $(( (x=$(pop)-$(pop))<0?-x:x ))
  ## NOTE arithmetic operators actually:
  ## check if size>=2 before;
  ## NOP if size<2
  .a\001.-XX.z
  # dup; push 1;
  # while size<2||[ $(pop) -gt $(pop) ]; do
  .a.c\001.yXX.z
    # pop;
    ## NOTE literal is pushed from backwards
    # for i in 32 44 --- 71; do push $i; done
    .a.sGo to the store and buy some more, XX.z
    # while !empty; do putchar $(pop); done
    .a.pXX.z
    # push 10; push 46; push 99;
    # call
    .ac.\n.eXX.z
    # exit
    .a.dXX.z
  # done
  .a.aXX.z
  # if !empty; do heap=$(pop); fi
  # for i in 32 44 --- 84; do push $i; done
  .a.tTake one down and pass it around, XX.z
  # while !empty; do putc $(pop); done
  .a.pXX.z
  # case ${heap:+y} in (y)
  #   push $heap;; esac;
  # call; push 46; putc $(pop);
  # push 10; putc $(pop)
  # push 10; putc $(pop)
  .a.m.e..o\n.o\n.oXX.z
# done
.a.a

Content
XX.z
## NOTE subroutine stack is initialized
## to be same as that of its caller
## but its heap is initialized to be empty,
## which is not shared to its caller
## So, implicitly: stack="$*"
## NOTE when returning to subroutine,
## heap is discarded but items on its
## stack is pushed to its caller, from
## bottom to top
# dup; push 100;
# while size<2||[ $(pop) -gt $(pop) ]; do
.a.cd.yXX.z
  # heap=$(pop)
  .a.tXX.z
  # for i in 108 108 --- 32; do push $i; done;
  # push $heap;
  # push 113; push 88; push 88
  .a on the wall.mXXqXX.z
# done
.a.aXX.z
# pop; dup; printf %d $(pop)
.a.s.c.nXX.z
# heap=$(pop)
.a.tXX.z
# for i in 114 101 --- 32; do push $i; done;
# push $heap; push 1;
# while size<2||[ $(pop) -ne $(pop) ]; do
.a of beer.m\001.zXX.z
  # push 115; push 88; push 88
  .aXXXXs.z
# done
.a.aXX.z
# for i in 101 --- 32; do push $i; done;
# while !empty; do putc $(pop); done
.a bottle.pXX.z
# (* implicitly *) return $stack (* to its caller *)
.a

Try it online!

M4, 224 bytes
define(b,`$1 bottle`'ifelse($1,1,,s) of beer')define(w,`b($1) on the wall')define(f,`w($1), b($1).
ifelse($1,1,`Go to the store and buy some more, w(99).',`Take one down and pass it around, w(decr($1)).

f(decr($1))')')f(99)

If I learn GNU extensions, perhaps I would shorten this code.
Try it online!

Knight, 170 169 bytes
;=r 99WrO++++++""r+C=sB=bS" bottles of beer"7?1r""=e" on the wall"+", "r+bI=r-rT+".
Take one down and pass it around, "r".
Go to the store and buy some more, 99"Cs+e".
"

Try it online!

1 byte: inline CALL

Ungolfed but still barely understandable
# set our counter to 99 (r)
; = counter 99
# We call this inline in the golfed code
# getbottle(s):
# returns pluralized " bottle(s) of beer" depending
# on counter's value, also storing it in beer
; = getbottles BLOCK {
    : = beer
      : SUBSTITUTE (
        : " bottles of beer"
        : 7
        # when counter == 1, remove the s
        : ? 1 counter
        # replace with empty string
        : ""
      : )
: }
# Loop while counter != 0
: WHILE counter {
    # basically concatenate everything
    # still better than StringBuilder
    : OUTPUT
     : ++++++
       : ""
       : counter
        : + (CALL getbottles)
          : (= wall " on the wall")
         : + ", "
           : counter
          : + beer
            : IF (= counter - counter 1) {
            :   # note: \n doesn't actually work, it is just for clarity
            :   + ".\nTake one down and pass it around, " counter
            : } {
            :   ".\nGo to the store and buy some more, 99"
            : }
           : + (CALL getbottles)
             : wall
            : ".\n"
: }

Vyxal Hmj, 128 bytes
ƛ‛ḋ¶øP«lT…|«"Ṅ£¥:«∧*JEp
'«\,+"Ṅ$"Ṅ‛.
+n‹[`¬Ȧ λḭ ∧ċ λ¬ ∆¶ it ⟑Ṡ`|«¢ṁĖ↲Ǐ¶QṪD£y∩i› kḭS√⁰«¡]‛, ++n‹[n‹|99]‛ḋ¶øP«∪ṠEȧ₆›λ*ĖC9«"Ṅ\.++;Ṙ

I was going to put this in a comment on @Wasif's answer, but the link was too long, so here's my Vyxal answer, with a -17 byte golf.
Try it Online!

Japt, 116 bytes
Needs more golfing or, perhaps, a completely different approach. Borrows a couple of tricks from ETH' original version.
Includes 2 trailing newlines.
´Lõ ÔË±` Þ¤{´LÎçs}  Þ8`".

{=D+`  e Ø!`}, {D}.
{LÎg`Go ‘ e ÐJe „d ¿y Ñ  Ú
Take e Üµ „d p†s Š ÂÐ˜`·}, "iUÃ¬é~H

Test it
´Lõ ÔË±`...{´LÎçs}...`"...{=D+`...`}...{D}...{LÎg`...`·}..."iUÃ¬é~H
´L                                                                      :Prefix decrement L (initially 100)
  õ                                                                     :Range [1,L]
    Ô                                                                   :Reverse
     Ë                                                                  :Map each D
      ±                                                                 :  Append
       `                                                                :  Compressed string
        ...                                                             :    " bottle"
           {                                                            :    Interpolate
            ´LÎ                                                         :      Prefix decrement L and get its sign
               çs                                                       :      Repeat "s" that many times
                 }                                                      :    End interpolate
                  ...                                                   :    " of beer"
                     `                                                  :  End compressed string
                      "...                                              :  Literal string (the return value)
                          {                                             :    Interpolate
                           =                                            :      Assign to variable U
                            D+`...`                                     :      D & the compressed string " on the wall"
                                   }                                    :    End interpolate
                                    ...                                 :    Literals
                                       {D}                              :    Interpolate D
                                          ...                           :    Literals
                                             {                          :    Interpolate
                                              LÎg                       :      Get the sign of L and index (0-based) into
                                                 `...`                  :        Compressed string "Go to the store and buy some more\nTake one down and pass it around"
                                                      ·                 :        Split on newlines
                                                       }                :    End interpolate
                                                        ..."            :  End of string
                                                            iU          :  Prepend U
                                                              Ã         :End map
                                                               ¬        :Join
                                                                é       :Rotate right by
                                                                 ~H     :  The bitwise NOT of 32 (i.e., -33)

Oracle SQL, 254 247 285 Bytes
Golfed:
SELECT REGEXP_REPLACE(n||b||w||', '||n||b||p||CHR(10)||DECODE(n,1,'Go to the store and buy some more, 99',t||(n-1))||b||w||p,'(^1| 1).*s','1 bottle') FROM(SELECT 100-LEVEL n,' bottles of beer'b,' on the wall'w,'Take one down and pass it around, 't,'.'p FROM DUAL CONNECT BY LEVEL<=99);

Ungolfed:
SELECT regexp_replace(n || b || w || ', ' || n || b || p || chr(10) || decode(n, 1, 'Go to the store and buy some more, 99', t || (n - 1)) || b || w || p,
                      '(^1| 1).*s',
                      '1 bottle')
FROM (SELECT 100 - LEVEL n,
            ' bottles of beer' b,
            ' on the wall' w,
            'Take one down and pass it around, ' t,
            '.' p
      FROM dual
      CONNECT BY LEVEL <= 99);

Basically, I get the row number via the hierarchical query and build a small library in the inner query.  Then I put it all together in the outer query.
Edit 1:
-7 Bytes: Used the "distributive" property on common string within the DECODE function.
Edit 2: +38 bytes: Oops, missed the s on 1 bottle. Fixed with regular expression string replacement.
Try it out here: DBFiddle

Rattle, 201 200 bytes
99& bottles of beer& on the wall&Take one down and pass it around, &, &.&1 bottle of beer&Go to the store and buy some more, 99|IPg[bb1b2b4bb1b5B-sb3[1f0]bb1b2b5Bbb^B]0;b6b2b5Bbb^Bb6b2b4b6b5Bb7b1b2b5q

Try it Online!
This basically takes the lyrics chopped up then pieces them together. It's likely that this can be shortened a bit

nroff, 259 bytes
.de B
\\$1
bottle
.if \\$1>1 \h'-1's
of beer
..
.de F
.B \\nX
on the wall,
.B \\nX
\h'-1'.
.sp 0
.nr X \\nX-1
.ie \\nX \\{Take one down and pass it around,
.B \\nX
\\}
.el \\{Go to the store and buy some more,
.B 99
\\}
on the wall.

..
.nr X 99
.while \nX .F

Attempt This Online!

Rust, 215 bytes
fn main(){for a in!297..{print!("{} bottle{}{}",99-a%!1%2383/3,&"s of beer on the wall"[a%!1/!4..87>>a%3&29],[", ",".
Take one down and pass it around, ",".

",".
Go to the store and buy some more, "][a%(6-3%!a)])}}

This is the smallest I could get with Rust... As for the standard version, it's 259 bytes (No solution, just proof). Both versions error with addition overflow... There's quite a bit of messy logic to filter the integer mess, still being smaller than a 0-300 loop.
It seems pretty competitive, even beating a Javascript answer?

Uiua, 192 bytes
o←:" of beer on the wall"
a←⊂:⊂" bottle"⟨""|@s⟩⊃(>1|°⋕)
b←⊂⟨"Take one down and pass it around, "a-1|"Go to the store and buy some more, "a99⟩=1.
l←&p$"__, _ of beer.\n__.\n"o⊃(.a|b)o
⍚l⇌+1⇡99

158

Uiua, 157 bytes
&p⊂⊂:↙30.↘67∧(⊂$"Take one down and pass it around, __.\n\n__, _.\n",,," on the wall"$"_ bottle_ of beer":▽:@s≠1.)+1⇡99"Go to the store and buy some more, "@.

Try it
Explanation:
+1⇡99"Go to the store and buy some more, "@.

Set up the stack with (bottom to top) a . character, the last part of the text, and the list of numbers 1 to 99 inclusive.
∧( ... )

Fold the range using that string as the initial value by the following function:

$"_ bottle_ of beer":▽:@s≠1.

Repeat s by whether the number is 1 or not, then put that in a format string with the number to get the "[N] bottle[s] of beer" part.

,,," on the wall"

Push "on the wall". The commas reorient the stack so that it will have (from bottom to top) "n bottles of beer" " on the wall" "n bottles of beer" " on the wall" "n bottles of beer".

$"Take one down and pass it around, __.\n\n__, _.\n"

Fill these into the format string. Every _ is where a stack item goes, starting from the left. Note that this starts with the "Take one down and pass it around" bit, so we'll have to remove that from the first line later.

⊂

Prepend this string to the fold value.

↘67

Drop 67 characters from the start of the string, removing the first "Take one down..." line.
↙30.

Push a copy of the whole string and take 30 characters from the start, yielding "99 bottles of beer on the wall"
&p⊂⊂:

Join this and the big string together, and append the . from earlier. Finally print this whole thing to STDOUT.

Pip, 147 bytes
L DhP[V Y{[h|99" bottle"h=1?x's" of beer"]}w:" on the wall"k;Vy'.nDh?"Take one down and pass it around""Go to the store and buy some more"k;Vyw'.n]

Attempt This Online!
Explanation
We use h as the loop counter. It is a variable predefined to 100, so the first thing we do is Decrement it to 99 and then Loop that many times.
Each time through the loop, we Print the contents of a big list (automatically concatenated together, with a trailing newline):

V Y{[h|99" bottle"h=1?x's" of beer"]}: the appropriate variation on "N bottle(s) of beer" based on the current value of h (see below)
w: " on the wall"
k: ", " (a predefined variable)
Vy: evaluate the "N bottle(s) of beer" code again
'.: "."
n: newline (a predefined variable)
Dh?"Take one down and pass it around""Go to the store and buy some more": decrement h; "Take one down and pass it around" if it's still truthy (nonzero), otherwise "Go to the store and buy some more"
k, Vy, w, '., n: as above

{[h|99" bottle"h=1?x's" of beer"]}
 [                              ]  ; Return this list (which gets concatenated when output):
  h|99                             ; h, or else 99 if h is 0
      " bottle"                    ; That string
               h=1?x's             ; If h is 1, empty string; otherwise, "s"
                      " of beer"   ; That string

C++, 335 bytes
Golfed:
#include <stdio.h>
int main(){for (int c = 99; c >= 2; c--){printf("%i bottles of beer on the wall, %i bottles of beer.\nTake one down and pass it around, %i bottles of beer on the wall.", c, c, c-1);}printf("1 bottle of beer on the wall, 1 bottle of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.");return 0;}

Ungolfed:
#include <stdio.h>

int main()
{
    for (int c = 99; c >= 2; c--) {
        printf("%i bottles of beer on the wall, %i bottles of beer.\nTake one down and pass it around, %i bottles of beer on the wall.", c, c, c-1);
    }
    printf("1 bottle of beer on the wall, 1 bottle of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.");

    return 0;

}

This does not have valid output for the last line.

Welcome to PPCG! I've done a bit of formatting, hope you don't mind. Unfortunately though, as VoteToClose has kindly pointed out, the last line is incorrect (and also two other lines before that, which require 1 bottle of beer rather than bottles).

Oh, thank you. I will fix as necessary.

This is still not valid. The last line is "Go to the store and buy some more, 99 bottles of beer on the wall.".

R, 297 bytes
b='bottles of beer'
w='on the wall'
p=paste
g=gsub
s='$'
x='.'
d=p(g(s,',',b),w)
j=99
i=j:1
l=g(s,x,p(i,d,i,b))
l[j]=g('s','',l[j])
m=p('Take one down and pass it around,',i,d)
o=p(l,m,sep="\n")
o[j]=g('\n.*',g(s,x,p('\nGo to the store and buy some more,',j,d)),o[j])
write(o,"")

Ideone link to test

Dart, 218 bytes
int i=99;b(i)=>'$i bottle${i<2?'':'s'} of beer';w(i)=>b(i)+' on the wall';main(){while(i>0){print(w(i)+', ${b(i)}.\n${--i>0?'Take one down and pass it around, '+w(i):'Go to the store and buy some more, '+w(99)}.\n');}}

Scala, 313 298 271 268 bytes
I think I may have reached the limit on this without resorting to string compression algorithms.
object m extends App{val(f,c)=((x:Int)=>s"$x bottle${if(x>1)"s" else ""} of beer"," on the wall");99 to 1 by -1 map(a=>println(s"${f(a)}$c, ${f(a)}.\n${if(a>1)"Take one down and pass it around" else "Go to the store and buy some more"}, ${f(if(a>1)a-1 else 99)}$c."))}

(Semi-)readable version:
object m extends App{
  val(f,c)=((x:Int)=>s"$x bottle${if(x>1)"s" else ""} of beer"," on the wall");
  99 to 1 by -1 map(a=>println(s"${f(a)}$c, ${f(a)}.\n${if(a>1)"Take one down and pass it around" else "Go to the store and buy some more"}, ${f(if(a>1)a-1 else 99)}$c."))
}

CJam, 257 229 bytes
229 Bytes
99:A;"bottles of beer":B;{A" "B" on the wall, "A" "B"."NA(:A;"Take one down and pass it around, "A" "B" on the wall."NN}98*"1 bottle of beer on the wall, 1 bottle of beer."N"Go to the store and buy some more, 99 "B" on the wall."

257 Bytes
99:A;{A" bottles of beer on the wall, "A" bottles of beer."NA(:A;"Take one down and pass it around, "A" bottles of beer on the wall."NN}98*"1 bottle of beer on the wall, 1 bottle of beer."N"Go to the store and buy some more, 99 bottles of beer on the wall."

Loader, 515 bytes
Run from a module named m.
~I:set I,B =99
set P =B
decr P
~P:printf "1 bottle of beer on the wall, 1 bottle of beer."
~P:printf "You go to the store and you buy some more, 99 bottles of beer on the wall."
~P:exit
P:printf "%n bottles of beer on the wall, %n bottles of beer." B,B
~P:printf "1 bottle of beer on the wall!, 1 bottle of beer."
decr P
decr B
P:printf "You take one down and you pass it around, %n bottles of beer on the wall." B
~P:printf "You take one down and you pass it around, 1 bottle of beer on the wall."
printf ""
load m

Python 3, 210 bytes
b=" of beer"
s="bottles"+b
t=s[:6]+b
q="on the wall."
p=print
x=99
while x>1:p(x,s,q,x,s+".\nTake one down and pass it around,",x-1,(s,t)[x>1],q);x-=1
p(1,t,q,1,t+".\nGo to the store and buy some more,",99,s,q)

Try it here: https://ideone.com/iX4byJ

This doesn't separate the verse by newlines, and it has a period instead of a comma after the wall on the first line

Python 3, 234 bytes
i=99
s='s'
b='%s bottle%s of beer'
o=' on the wall'
y=b%(i,s)
while i:print(y+o+', '+y+'.');i-=1;s=('',s)[i>1];y=b%(i,s);print(('Take one down, pass it around, '+y+o+'.\n','Go to the store and buy some more, 99'+b%(s,'s')+o+'.')[i<1])

with newlines for some more readability
i=99;
s='s';
b='%s bottle%s of beer';
o=' on the wall';
y=b%(i,s)
while i:
  print(y+o+', '+y+'.');
  i-=1;
  s=('',s)[i>1];
  y=b%(i,s);
  print(('Take one down, pass it around, '+y+o+'.\n','Go to the store and buy some more, 99'+b%(s,'s')+o+'.')[i<1])

makes use of a quirk of booleans to evaluate to zero or one; this allows them to be used as index in two value tuples or lists, and are shorter than the 'proper' equivalent. These pseudo ternaries are used to keep all the loop code on one line.

F#, 210 201 263 241 bytes
let B n=sprintf"%d bottle%s of beer"n (if n>1 then"s"else"")
for n=99 downto 1 do printfn"%s on the wall, %s.\n%s on the wall.\n"(B n)(B n)(if n>1 then"Take one down and pass it around, "+B(n-1)else"Go to the store and buy some more, "+B 99)

Missed the "Go to the store and buy some more" part ..

C(GCC), 389 bytes
char*c="%1d %3$s of %4$s on the %5$s, %1d %3$s of %4$s.\n\0Take one down and pass it around, %2d %3$s of %4$s on the %5$s.\n\0Go to the store and buy some more, %1d %3$s of %4$s on the %5$s.\n";main(){int k=99;while(k!=0){printf(&c[0],k,k,k==1?"bottle":"bottles","beer","wall");if(1){printf(&c[49],k,k,"bottles","beer","wall");k--;}else{k=99;printf(&c[114],k,k,"bottles","beer","wall");}}}

Used some things I never thought I'd use. I actually don't know if it is supposed to loop after the end.
Ungolfed version:
char* c = "%1d %3$s of %4$s on the %5$s, %1d %3$s of %4$s.\n\0Take one down and pass it around, %2d %3$s of %4$s on the %5$s.\n\0Go to the store and buy some more, %1d %3$s of %4$s on the %5$s.\n";
main() {
    int k = 99;
    while (1) {
        printf(&c[0], k, k, k == 1 ? "bottle" : "bottles", "beer", "wall");
        if (k != 1) {
            printf(&c[49], k, k, "bottles", "beer", "wall");
            k--;
        } else {
            k=99;
            printf(&c[114], k, k, "bottles", "beer", "wall");
        }
    }
}

This can be still golfed more.

TLDCode, 235 bytes
='1'#99#p${e# bottle#PG(#s#P)v# of beer on the wall, #Pe# bottle#PG(#s#P)v# of beer.#Pn-(#Take one down and pass it around, #Pe# bottles of beer on the wall.#Pnn)[#Go to the store and buy some more, 99 bottles of beer on the wall.#P]}=

we first clear the stack of the implicit input
We start by pushing a 1 onto the stack, then pushing 99, since that is the first line.
Then we define a repeatable code block that will repeat the amount of times equal to the value of the top of the stack (99)
The first thing that is done on each repeat is printing the top of the stack.
Followed by pushing and printing" bottle" and then if the top of the stack(99) is greater than the next in the stack(1) push and print an "s". Then we remove the top of the stack(the result of the comparison)
Push and print " of beer on the wall, " followed by the printing the top of the stack(99).
then we do step 4 again
then we push and print " of beer." followed by printing a new line
we then decrement the top of the stack (98)
we then define a block that only executes if the top of the stack is true, or an integer greater than 0
this just pushes and prints the second line
then we define a block that only executes if the top of the stack is false or not greater than 0
this pushes the last line
we clear the stack to avoid the implicit printing

Julia 0.6.0 (256 bytes)
b=" bottle";d=" of beer";w=" on the wall, ";t="Take one down and pass it around,";i=99;k=println;while i>0 (i>1)&&(k(i,b*"s",d,w,i,b*"s",d);k(t,i,b*"s",d,w));(i==1)&&(k(i,b,d,w,i,b,d));i-=1 end;k("Go to the store and buy some more, ",99,b*"s",d,w[1:end-2])
I know there is already a Julia answer, but I wanted to tackle it anyway! I only have a few bytes more then the other Julia so maybe I can find a way to chop some here and there!
Try it online!

[Jq 1.5], 236 232 bytes
def B:" bottle\(if.>1 then"s"else""end) of beer";def W:" on the wall";def T:"Take one down and pass it around";99-range(99)|"\(.)\(B+W), \(.)\(B).
\(T), \(.-1)\(.-1|B+W).
"|gsub(0|T+", 0"+B+W;"Go to the store and buy some more, 99"+B+W)

Expanded
def B:" bottle\(if.>1 then"s"else""end) of beer";
def W:" on the wall";
def T:"Take one down and pass it around";

  99-range(99)
| "\(.)\(B+W), \(.)\(B).
\(T), \(.-1)\(.-1|B+W).
"
|gsub(0|T+", 0"+B+W;"Go to the store and buy some more, 99"+B+W)

Try it online!
Ty GammaFunction for catching a mistake.

Errantly outputs 1 bottles in the last line of the second-to-last verse.

Thanks... will correct

Funky, 290 bytes
for(i=99;i>0;i--)print("%i %s on the wall, %i %s.%sTake one down and pass it around %i %s on the wall."::format(i,c=(b="bottle%s of beer")%(i>1)?"s":"",i,c,"\n",i,c))print("0 %s on the wall, 0 %s.%sGo to the store and buy some more, 99 bottles of beer on the wall."::format(c=b%"s",c,"\n"))

Try it online!

17, 1180 bytes
0 {
2 #
1 -
:
2 @
3 <
0 @
1f : : : : : : : : : : : : : : : : : : : : : :
2 # $$
$ 5d $ 69 $ 6e $ 6e $ 66 $ 5g $ 6d $ $ 69 $ 60 $ $ 5d $ 5g $ 5g $ 6c $ $ 69 $ 68 $ $ 6e $ 62 $ 5g $ $ 70 $ 5c $ 66 $ 66 $ 2a $ $
2 # $$
$ 5d $ 69 $ 6e $ 6e $ 66 $ 5g $ 6d $ $ 69 $ 60 $ $ 5d $ 5g $ 5g $ 6c $ 2c $ a $
4g $ 5c $ 65 $ 5g $ $ 69 $ 68 $ 5g $ $ 5f $ 69 $ 70 $ 68 $ $ 5c $ 68 $ 5f $ $ 6a $ 5c $ 6d $ 6d $ $ 63 $ 6e $ $ 5c $ 6c $ 69 $ 6f $ 68 $ 5f $ 2a $ $
2 # 1 - $$
$ 5d $ 69 $ 6e $ 6e $ 66 $ 5g $ 6d $ $ 69 $ 60 $ $ 5d $ 5g $ 5g $ 6c $ $ 69 $ 68 $ $ 6e $ 62 $ 5g $ $ 70 $ 5c $ 66 $ 66 $ 2c $ a $ a $
}
1 {
1f : : : : : : : : : : : : : : : : : : : : : : :
2f $ $ 5d $ 69 $ 6e $ 6e $ 66 $ 5g $ $ 69 $ 60 $ $ 5d $ 5g $ 5g $ 6c $ $ 69 $ 68 $ $ 6e $ 62 $ 5g $ $ 70 $ 5c $ 66 $ 66 $ 2a $ $ 2f $ $ 5d $ 69 $ 6e $ 6e $ 66 $ 5g $ $ 69 $ 60 $ $ 5d $ 5g $ 5g $ 6c $ 2c $ a $
43 $ 69 $ $ 6e $ 69 $ $ 6e $ 62 $ 5g $ $ 6d $ 6e $ 69 $ 6c $ 5g $ $ 5c $ 68 $ 5f $ $ 5d $ 6f $ 72 $ $ 6d $ 69 $ 67 $ 5g $ $ 67 $ 69 $ 6c $ 5g $ 2a $ $ 36 $ 36 $ $ 5d $ 69 $ 6e $ 6e $ 66 $ 5g $ 6d $ $ 69 $ 60 $ $ 5d $ 5g $ 5g $ 6c $ $ 69 $ 68 $ $ 6e $ 62 $ 5g $ $ 70 $ 5c $ 66 $ 66 $ 2c $ a $
0 @
}
777 {
5f 2 @
0 1 @
0 0 @
}

Gema, 210 206 characters
\A=@set{b;99}@repeat{99;@w{}, @b{}.\n@cmpn{$b;1;;Go to the store and buy some more@set{b;99};Take one down and pass it around@decr{b}}, @w{}.\n\n}@end
b:=$b bottle@cmpn{$b;1;;;s} of beer
w:=@b{} on the wall

Sample run:
3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.

2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.

1 bottle of beer on the wall, 1 bottle of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.

T-SQL, 334 bytes
WITH n AS(SELECT 99c UNION ALL SELECT c-1FROM n WHERE c>2),s
AS(SELECT' bottles of beer'b,' on the wall'w,'.
Take one down and pass it around, 't)SELECT
CONCAT(c,b,w,', ',c,b,t,c-1,IIF(c=2,STUFF(b,8,1,''),b),w,'.')FROM n,s
UNION ALL SELECT REPLACE(CONCAT(1,b,w,', ',1,b),'s','')+'.
Go to the store and buy some more, 99'+b+w+'.'FROM s

Inspired by but different than both other T-SQL answers. Note that both these answers  have a bug on the 2nd to last verse, they output 1 bottles instead of 1 bottle. If I allow that bug in my code, too, I can get down to 309 bytes by removing IIF(c=2,STUFF(b,8,1,''),b).
In my case I'm joining together a table of numbers and a table of strings, and using the CONCAT() function to assemble each verse.
I then construct the very last verse separately using UNION ALL. REPLACE() is used to make both of the bottle singular.

Perl 6, 226 bytes
sub b(\a,$b?){a~' bottle'~('s'if a-1)~' of beer'~(' on the wall'if $b)}
for 99…1 ->\a{say b(a,1)~", "~b(a)~'.
'~(if a-1 {'Take one down and pass it around, '~b a,1;}else{'Go to the store and buy some more, '~b 99,1;})~'.
';}

Try it online!

Some quick golfing for 207 bytes. There's more bytes to be saved though

ES7, 275 bytes
for(x=99,a=``;x;)a+=x+` bottles of beer on the wall, ${x} bottles of beer,
Take one down and pass it around, ${x--} bottle${x-1?"s":""} of beer on the wall.
`;a+=`1 bottle of beer on the wall, 1 bottle of beer,
Go to the store and buy some more, 99 bottles of beer on the wall.`

Feel free to find bugs and to give ideas on how to optimize the code.

Javascript ES6, 247 237 bytes
m=" bottles of beer",j=m.replace("s",""),k=" on the wall",a="";for(i=99;i>1;)a+=i+m+k+`, ${i+m}.
Take one down and pass it around, ${(--i)+(i-1?m:j)+k}.

`;a+=`1${j+k}, 1${j}. Go to the store and buy some more, 99${m+k}.`;console.log(a);

-10 bytes thanks to Jo King.

ink, 192 bytes
-(t)->c->v->
, ->c->
-(k){k<99:
Take one down and pass it around, ->c->v->
.
\
->t
}Go to the store and buy some more, ->c->v->
.
=c
{99-k%99} bottle{k-98:s} of beer->->
=v
 on the wall<>->->

Try it online!
Not too bad for a non-esoteric language.
Explained
-(t)                                               // A gather point, with the label t. We can divert here, and t can be read as a view counter.
->c->v->                                           // Go to c, then v, then return here
, ->c->                                            // Print a comma and a space, then go to c and return here
-(k)                                               // Another labelled gather. Having the read counter be here is useful, because it means we handle the increment in the right spot.
{k<99:                                             // The first 98 times:
Take one down and pass it around, ->c->v->         // Take one down and pass it around. Also, go to c and v.
.                                                  // Print a period
\                                                  // Ink is a bit weird with whitespace, but this lets us force a newline. Note that there is one space after the backslash in the original.
->t                                                // Back to the top
}Go to the store and buy some more, ->c->v->       // We only get here when k>=99
.                                                  // Print a period, then halt because we're out of content.
=c                                                 // In this stitch, we print how many bottles of beer we have. We use the k counter to keep track.
{99-k%99} bottle{k-98:s} of beer->->               // The modulo is to reset to 99 when we get back from the store. We also almost always print the s after bottle - but not when k is 98.
=v                                                 // "on the wall"
 on the wall<>->->                                 // <> means don't print a newline after this string. You need to specify that sometimes.

Actually, 1 byte
N

Try it online!

There's already a Seriously answer here, and I'd say that it borders on breaking a standard loophole anyway, since the Bottles of Beer built-in was added to the language specifically for this challenge, which is why they are incorrect in the same ways (no zero bottle verse)

Hexadecimal Stacking Pseudo-Assembly Language, 2208 bytes
20000140030000000620100040000000001642030040040041000040010020000140000041010040000022000042000040000040000030000003000001001620006340000003040001000000000120001140010003040001000200001120006C40020020006C40020020006140020020007740020020002040020020006540020020006840020020007440020020002040020020006E40020020006F40020020002040020003040014020003040002010000000220001240010003040001000300001220007240020020006540020020006540020020006240020020002040020020006640020020006F40020020002040020003040014020003040002010000000342000040000003040012000020006540020020006C40020020007440020020007440020020006F40020020006240020020002040020003040014020042000040000042000040000020000140000030000020000040000030000003000002010020007340020003040014020003040002010000000442000040000020000040000030000020000040000030000003000001000520006440020020006E40020020007540020020006F40020020007240020020006140020020002040020020007440020020006940020020002040020020007340020020007340020020006140020020007040020020002040020020006440020020006E40020020006140020020002040020020006E40020020007740020020006F40020020006440020020002040020020006540020020006E40020020006F40020020002040020020006540020020006B40020020006140020020005440020003040014020003040002010000000520002040020020002C40020020006540020020007240020020006F40020020006D40020020002040020020006540020020006D40020020006F40020020007340020020002040020020007940020020007540020020006240020020002040020020006440020020006E40020020006140020020002040020020006540020020007240020020006F40020020007440020020007340020020002040020020006540020020006840020020007440020020002040020020006F40020020007440020020002040020020006F40020020004740020003040014020020006340000020001540010003040001000100001520002E40020003040014020003040001002700000020001040010003040001000100001020002040020020002C40020003040014020020002040010003040001000200002020000A40020020002E40020003040014020041000040010020000140000041010040000022000020003040010003040001000400003020002040020020002C40020003040014020020004040010003040001000100004020000A40020020000A40020020002E400200030400140200030400010000000007410200430200400000400000300000030000010007000017410400430400400000400000300000030000010017000027400300300303420300400300030300010006

Try it online!
That was quite some work, because the language has a few difficulties (or maybe just interpreter bugs)

You can only jump to labels you have been before, so basically you
have to run through the code, skipping every jump and print command,
and then you can run it
Subtracting is "backwards". For example, 100-1 is push 1, push
100, sub, what makes loops a bit difficult.
It can't handle negative values, so using -1 + 100 instead, isn't
possible. If you try to calculate 0-1, you will get 0 as the result.
The functions NOT and XOR aren't working (I didn't try if AND
and OR are working)
The only conditional is "skip next command, if popped value is not
zero", which is exactly opposite to all other esoteric languages I
know so far.
You have to push return-labels by yourself (and you can't return to a
label where you haven't been before)

So what the code does is, it sets up a list of 1's on the initializing-stack and uses them to skip every jump and print command, then it deletes the list, fills it with 0's and runs the main loop.
Stack 0: bottle count, Stack 1: return labels, Stack 2: Strings, Stack 3: Initialize flag, Stack 4: Initialize Queue

200001  1
400300  push initialize = 1

000006  initializer:
201000  0x1000 (init_counter)
400000  push
000016  initialize_loop:
420300  peek initialize
400400  push init_queue

410000  pop init_counter
400100  push on other stack
200001  1
400000  push
410100  pop init_counter
400000  push to counter_stack
220000  sub
420000  peek init_counter
400000  push
400000  push 0
300000  equals
030000  if init_counter > 0
010016  go to initialize loop

200063  99
400000  push
030400  if not initializing
010000  go to main

000001  Label wall:
200011  0x11 (return address)
400100  push retstack
030400  if not initializing
010002  jmp beer
000011  Label Return_beer_wall
20006C  'l'
400200  push
20006C  'l'
400200  push
200061  'a'
400200  push
200077  'w'
400200  push
200020  ' '
400200  push
200065  'e'
400200  push
200068  'h'
400200  push
200074  't'
400200  push
200020  ' '
400200  push
20006E  'n'
400200  push
20006F  'o'
400200  push
200020  ' '
400200  push
030400  if not initializing
140200  print string
030400  if not initializing
020100  return

000002  Label beer:
200012  0x12 (return address)
400100  push retstack
030400  if not initializing
010003  jmp bottles
000012  Label Return_bottles_beer
200072  'r'
400200  push
200065  'e'
400200  push
200065  'e'
400200  push
200062  'b'
400200  push
200020  ' '
400200  push
200066  'f'
400200  push
20006F  'o'
400200  push
200020  ' '
400200  push
030400  if not initializing
140200  print string
030400  if not initializing
020100  return

000003  Label bottles:
420000  peek bottle count
400000  push
030400  if not initializing
120000  print number
200065  'e'
400200  push
20006C  'l'
400200  push
200074  't'
400200  push
200074  't'
400200  push
20006F  'o'
400200  push
200062  'b'
400200  push
200020  ' '
400200  push
030400  if not initializing
140200  print string
420000  get bottle count
400000  push
420000  get bottle count
400000  push
200001  1
400000  push
300000  equals
200000  0
400000  push
300000  equals
030000  skip if true
020100  return
200073  's'
400200  push
030400  if not initializing
140200  print string
030400  if not initializing
020100  return

000004  Label Take:
420000  get bottle count
400000  push
200000  0
400000  push
300000  equals (bottle count = 0)
200000  0
400000  push
300000  equals (bottle count != 0)
030000  if bottle count = 0
010005  jmp Store
200064  d
400200  push
20006E  n
400200  push
200075  u
400200  push
20006F  o
400200  push
200072  r
400200  push
200061  a
400200  push
200020
400200  push
200074  t
400200  push
200069  i
400200  push
200020
400200  push
200073  s
400200  push
200073  s
400200  push
200061  a
400200  push
200070  p
400200  push
200020
400200  push
200064  d
400200  push
20006E  n
400200  push
200061  a
400200  push
200020
400200  push
20006E  n
400200  push
200077  w
400200  push
20006F  o
400200  push
200064  d
400200  push
200020
400200  push
200065  e
400200  push
20006E  n
400200  push
20006F  o
400200  push
200020
400200  push
200065  e
400200  push
20006B  k
400200  push
200061  a
400200  push
200054  T
400200  push
030400  if not initializing
140200  print string
030400  if not initializing
020100  return

000005  Label Store:
200020  ' '
400200  push
20002C  ','
400200  push
200065  e
400200  push
200072  r
400200  push
20006F  o
400200  push
20006D  m
400200  push
200020
400200  push
200065  e
400200  push
20006D  m
400200  push
20006F  o
400200  push
200073  s
400200  push
200020
400200  push
200079  y
400200  push
200075  u
400200  push
200062  b
400200  push
200020
400200  push
200064  d
400200  push
20006E  n
400200  push
200061  a
400200  push
200020
400200  push
200065  e
400200  push
200072  r
400200  push
20006F  o
400200  push
200074  t
400200  push
200073  s
400200  push
200020
400200  push
200065  e
400200  push
200068  h
400200  push
200074  t
400200  push
200020
400200  push
20006F  o
400200  push
200074  t
400200  push
200020
400200  push
20006F  o
400200  push
200047  G
400200  push
030400  if not initializing
140200  print string
200063  99
400000  push bottle counter
200015  0x15 (return address)
400100  push retstack
030400  if not initializing
010001  jmp wall
000015  Label Return_wall_Store
20002E  '.'
400200  push
030400  if not initializing
140200  print string
030400  if not initializing
010027  go to end_program

000000  Label Main:
200010  0x10 (return address)
400100  push retstack
030400  if not initializing
010001  jmp wall
000010  Label Return_wall_main
200020  ' '
400200  push
20002C  ','
400200  push
030400  if not initializing
140200  print string
200020  0x20 (return address)
400100  push retstack
030400  if not initializing
010002  jmp beer
000020  Label Return_beer_main
20000A  <nl>
400200  push
20002E  '.'
400200  push
030400  if not initializing
140200  print string
410000  pop bottle count
400100  push on other stack
200001  1
400000  push
410100  pop bottle count
400000  push to original stack
220000  sub
200030  0x30 (return address)
400100  push retstack
030400  if not initializing
010004  jmp Take
000030  Label Return_Take_main
200020  ' '
400200  push
20002C  ','
400200  push
030400  if not initializing
140200  print string
200040  0x40 (return address)
400100  push retstack
030400  if not initializing
010001  jmp wall
000040  Label Return_wall_main_2
20000A  <nl>
400200  push
20000A  <nl>
400200  push
20002E  '.'
400200  push
030400  if not initializing
140200  print string
030400  if not initializing
010000  jmp main

000007  flush_strings:
410200  pop character
430200  get strings-stack size
400000  push
400000  push 0
300000  equals
030000  if characters left
010007  go to flush_strings
000017  flush_init_queue:
410400  pop entry
430400  get init_queue_stack size
400000  push
400000  push 0
300000  equals
030000  if entries left
010017  go to flush_init_queue

000027  end_program:
400300  push 0
300303  equals
420300  peek initialize
400300  push initialize
030300  if not initialize
010006  go to initializer

C# (Visual C# Interactive Compiler), 243 bytes
for(int i=99;i>0;){string s="s",a=" bottle",b=" of beer",w=" on the wall",c=$"{i+a+(i>1?s:"")+b}";Write(@$"{c+w}, {c}.
{(--i>0?$"Take one down and pass it around, {i+a+(i>1?s:"")+b+w}":$"Go to the store and buy some more, 99{a+s+b+w}")}.

");}

Try it online!

C (gcc), 229 bytes
#define B" bottle%c of beer"
#define W" on the wall"
f(x){for(x=99;x;)printf("%d"B W", %1$d"B".\n%s, %d"B W".\n\n",x+1,'s'*!!x,'s'*!!x,x?"Take one down and pass it around":"Go to the store and buy some more",x?x:99,--x-1?'s':0);}

Needs more golf...
Try it online!

222 bytes

Duocentehexaquinquagesimal, 279 bytes
`Y7íY-þÌÅÈµ3₃1œ₆"Σè‹×ê8ëbc¾½}tiQιδcâāιÎ–´K2X©∍U7ΘËð»¦Õe˜ãΓh=_²₆GÜƒ‚ÄÏ4ÒþO@æc†5ˆ‹₄ý¡)Esƶ’ĆË₁^rgÝyúÃćÆa^}λв?‰ZJ~‡çN©ƶƶнîõgoÎˆy«N§Lÿ£&Ćƶ¯…sHkHÆ#нåY~gûîf6!0§`zнÕlʒ3ǝú×…ù[9»¦\ÌååÌ}ι|~qsŒnNdΔd’â*›nQáîÃî~j™∍ît‹ó0uº5-˜ÐØîĆe&U!z©ÅñÒ(„†{k4Zéм›}%1₅cʒ€bιG6c‚¤[øÚYR:ñŒäηíMÝVηí\δÇñ5æƶÖ|›5WìÌ¦z

Try it online!

Pinecone, 216 bytes
l:" on the wall";k:" bottles of beer";i:99|i>0|i:i-1@(j:(i.String);print:j+k+l+", "+j+k+".";i=1?print:"Go to the store and buy some more, 99"+k+l+"."|print:"Take one down and pass it around, "+((i-1).String)+k+l+".")

Seriously, 1 byte
N

Try it online!

oh yes, seriously one byte

Pyt, 855 857 bytes
2ᴇ⁻⁻`⁺ĐĐ₀7²⁻+⇹1ᴇ%7²⁻+5²Đ2⁵2ᴇ⁻⁻3⑴ǰƖ5!4-ĐĐ8-2ᴇ⁺Đ77++2⁵3⑴ǰƖ2ᴇ⁺⁺2⁵7²2*2ᴇ⁺ĐĐ67++2⁵3⑴ǰƖĐ⁻2⁵5!4-2ᴇ4+Đ3-2⁵5!⁻2ᴇ3-5!4!₂-Đ9△⁻2⁵áĐ0⦋áƑÁĐ₀7²⁻+⇹1ᴇ%7²⁻+2⁵2ᴇ⁻⁻3⑴ǰƖ5!4-ĐĐ8-2ᴇ⁺Đ77++2⁵3⑴ǰƖ2ᴇ⁺⁺2⁵7²2*2ᴇ⁺ĐĐ67++9△⁺1ᴇ9²3+2ᴇ3-2ᴇ7+2ᴇ⁺2⁵3⑴ǰƖĐ⁻2ᴇ⁺2⁵2ᴇ3⑴ǰƖ5!⁻Đ9-9△⁻2⁵3⑴ǰƖ⁺2ᴇ3-5!5-Đ2⁵2ᴇ5+5!4-2⁵2ᴇ3-5!6-Đ3-Đ6+Đ7-2ᴇ9△⁻2⁵áĐ0⦋⁻áƑÁĐ₀7²⁻+⇹1ᴇ%7²⁻+2⁵2ᴇ⁻⁻3⑴ǰƖ5!4-ĐĐ8-2ᴇ⁺Đ77++2⁵3⑴ǰƖ2ᴇ⁺⁺2⁵7²2*2ᴇ⁺ĐĐ67++2⁵3⑴ǰƖĐ⁻2⁵5!4-2ᴇ4+Đ3-2⁵5!⁻2ᴇ3-5!4!₂-Đ9△⁺1ᴇáĐĐŁř2≥*Đ1⦋68*≠?ŕ:ŕĐŁř2≠*Đ6²⁻⦋68*≠?ŕ:ŕĐŁř6²≠*Đ9²5+⦋68*≠?ŕ:ŕĐŁř9²6+≠*;Đ9²5+⦋68*=?ŕĐŁř9²6+≠*:ŕ;žƇǰƥ0⦋⁻⁻łŕ7²2⁵2ᴇ⁻⁻3⑴ǰƖ5!4-ĐĐ8-2ᴇ⁺2⁵3⑴ǰƖ2ᴇ⁺⁺2⁵7²2*2ᴇ⁺ĐĐ67++2⁵3⑴ǰƖĐ⁻2⁵5!4-2ᴇ4+Đ3-2⁵5!⁻2ᴇ3-5!4!₂-Đ9△⁻2⁵7²2⁵2ᴇ⁻⁻3⑴ǰƖ5!4-ĐĐ8-2ᴇ⁺2⁵3⑴ǰƖ2ᴇ⁺⁺2⁵7²2*2ᴇ⁺ĐĐ67++9△⁺1ᴇ7⬠⁺3⑴ǰƖ2⁵5!4-Đ5-2⁵5!4-2ᴇ4+Đ3-2⁵5!5-Đ⁺Đ5-Đ3+2ᴇ⁺2⁵2ᴇ3-2ᴇ1ᴇ+2ᴇ2⁵7²2*5!3-Đ4+2⁵5!5-Đ4-Đ2-Đ8-2⁵2ᴇ9+3⑴ǰƖĐ3+2ᴇ⁺9△⁻2⁵6⬠6+Đ2⁵2ᴇ⁻⁻3⑴ǰƖ5!4-ĐĐ8-2ᴇ⁺5!5-2⁵3⑴ǰƖ2ᴇ⁺⁺2⁵7²2*2ᴇ⁺ĐĐ67++2⁵3⑴ǰƖĐ⁻2⁵5!4-2ᴇ4+Đ3-2⁵5!⁻2ᴇ3-5!4!₂-Đ9△⁺áƇǰ;

Try it online!
As Pyt code does not include strings, the entire song has to be built letter by letter. The first half or so of the code (from the beginning until the ł) prints all but the last verse. The rest of the code prints the last verse.
I'm sure this can be golfed; I'm really just too lazy (plus I've already spent like 3 hours on it and I need a break).

Python 3.8 (pre-release), 237 bytes
for i in range(99,0,-1):print(f"{i}"+(x:=f" bottle{'s'*(i>1)} of beer on the wall")+", "+f"{i}"+x[:16-(i<2)]+".\n",f"Take one down and pass it around, {i-1-(i<3)}{x}.\n"if i>1else f"Go to the store and buy some more, 99 bottles{x[7:]}.")

Try it online!

YASEPL, 264 bytes
=n$100=p)"s"=q)p=w=o)" bottle"=d)comma`1!-[4!p)""`4!w$n-[5!q)""`5#n#o#p#" of beer on the wall"#d#space#n#o#p>" of beer."!@2#"Take one down and pass it around"#d|3`2#"Go to the store and buy some more"#d!q)"s"!w$99`3#space#w#" bottle"#q>" of beer on the wall.">""![

Bash, 268, 261, 247, 244, 241 bytes
f(){
(($1>1))&&s=s
[ $2 ]||a=" on the wall"
echo "$1 bottle$s of beer$a"
}
for((n=$1;n;n--));{
b="Take one down and pass it around,"
j=$[n-1]
((j<1))&&b="Go to the store and buy some more," j=$1
echo -e "$(f $n), $(f $n 1).\n$b $(f $j).\n"
}

Try it online!

In ((n>0)) the comparison is pointless, ((n)) is enough. In of\ beer\ on\ the\ wall there are more backslashes than single quotes in 'of beer on the wall'. ((n--))((n>1)) can join in a single expansion as ((--n>1)). The 3 \ns in printf's argument can be replaced with literal line breaks.

As the while is the same size as a single argument for, is recommended the use the later because with that you can replace the following do .. done with { .. }. Beside that, you can move n's initialization as fors parameter: for((n=$1;n;));{ .. }

In everyday scripting is indeed strongly recommended to use $( .. ) for command substitution, but for golfing \`` .. `is shorter. Again in((j<1))you can get rid of comparison, but as((j))reverses the logic, you need to reverse the following list control operator into||`.

May depend on the system you use, but with the echo you usually can find on Linuxes the -e switch is pointless there. And the 1st echo's arguments need no quoting.

There would be a twist applying the use alternate value parameter expansion on f()'s parameter $2, but would be difficult to explain in words, so here you can see it: Try it online!

@manatwork cool, thank you

R, 269 bytes
R sometimes can process text without much pain. In this case can't, but the iteration system rocks:
cat(sprintf('\n%s bottles of beer on the wall, %s bottles of beer.Take one down and pass it around, %s bottles of beer on the wall.',99:2,99:2,98:1),'\n1 bottle of beer on the wall, 1 bottle of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.')

Output does not match challenge example and when there's 2 left: "2 bottles of beer on the wall, 2 bottles of beer.Take one down and pass it around, 1 bottles of beer on the wall. " there's an extraneouss at "1 bottles".

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/