Every example I\'ve seen of CGI/Perl basically a bunch of print statements containing HTML, and this doesn\'t seem like the best way to write a CGI app. Is there a better way t
Leaving the question of CGI vs MVC framework for the moment, what you're going to want is one of the output templating modules from the CPAN.
The Template Toolkit is very popular (Template.pm on CPAN) Also popular are Text::Template, HTML::Template, and HTML::Mason.
HTML::Mason is much more than a template module, and as such might be a little too heavy for a simple CGI app, but is worth investigating a little while you're deciding which would be best for you.
Text::Template is reasonably simple, and uses Perl inside the templates, so you can loop over data and perform display logic in Perl. This is seen as both a pro and con by people.
HTML::Template is also small and simple. It implements its own small set of tags for if/then/else processing, variable setting, and looping. That's it. This is seen as both a pro and a con for the exact opposite reasons as Text::Template.
Template toolkit (TT) implements a very large, perlish template language that includes looping and logic, and much more.
I used HTML::Template one, and found I wanted a few more features. I then used Text::Template with success, but found its desire to twiddle with namespaces to be a little annoying. I've come to know and love Template Toolkit. For me it just feels right. Your mileage may vary.
Of course, there is still the old "print HTML" method, sometimes a couple of print statements suffices. But you've hit upon the idea of separating your display from your main logic. Which is a good thing.
It's the first step down the road to Model/View/Controller (MVC) in which you keep separate your data model&business logic (your code that accepts the input, does something with it, and decides what needs to be output), your your input/output (Templates or print statements - HTML, PDF, etc.) , and the code that connects the two (CGI, CGI::Application, Catalyst MVC Framework, etc.). The idea being that a change to your data structure (in the Model) should not require changes to your output routines (View).