Perl: __DATA__ section

This is my first Perl post. I wanted to explore a less known feature of Perl which is very useful at times.

Lets say as in my case that you need to combine and then compress .js and .css files from all over the place.
What I mean is that instead of importing in your html multiple different files you want to just import one compressed javascript file and one compressed css file.

Your first instinct may be, is to create a hash and describe all files locations … but may be the simple and dirty approach is better, enter the Perl __DATA__ section.

It is very clever idea, what if you can treat part of the source code file as a different file i.e. file within a file. Simply said everything after the __DATA__ token could be accessed via the <DATA> file handle.

Saying : @array = <DATA> will slurp everything into the @array, one line per element. In the example below i’m also removing the new line symbol.

So how do we setup which files to compress ? Simple, first type __DATA__ at the end of the file and then just use any command to append the path/file spec. I used those :

  • ls -w1 -d $PWD/blah/*.js >> compress.pl
  • ls -w1 -d $PWD/blah/*.css >> compress.pl

… several times. Don’t forget use append >>, not overwrite >

What does the script do :

  • read the __DATA__ section into the @files array
  • filter .js and .css and create a space separated string
  • using the strings from above build commands to concatenate the two groups
  • run the tools to compress concatenated files
#!/usr/bin/env perl
use strict;

sub run {
	my $cmd = shift @_;
	print "> $cmd\n";
	qx/$cmd/
}

#concatenated files
my $cat_js = 'cat.js';
my $cat_css = 'cat.css';
my $out = 'out';
#define compress commands
my $js_cmd = "/usr/local/bin/uglifyjs -c -m -- $cat_js > $out.js";
my $css_cmd = "yui-compressor --type css $cat_css > $out.css";

#read the DATA section
my @files = map {s/\n//;$_} <DATA>;
#extract js and css file
my $js_files  = join ' ', (grep /js$/, @files);
my $css_files = join ' ', (grep /css$/, @files);

#concatenate files
my $js_cat  = "cat $js_files > $cat_js";
my $css_cat = "cat $css_files > $cat_css";

run($js_cat);
run($js_cmd);

run($css_cat);
run($css_cmd);

#ls -w1 -d $PWD/*.*  >> compress.pl

__DATA__

One more thing, what is the purpose of the first line …

Shebang

If you make the file executable and have :

#!/usr/bin/perl

or

#!/usr/bin/env perl

as a first line then instead of calling the script like this :

perl compress.pl

you can do it this way :

./compress.pl