Fork me on github!!

Dealing with shebangs

In the POD of perlrun, we can find two possible perl program preambles that tries to use the environment-decided perl installation, one is very cryptic:

#! −*−perl−*−
eval 'exec perl −x −wS $0 ${1+"$@"}'
    if 0;

The other is extremely simple:

#!/usr/bin/env perl

Basically they are both telling the OS (that understands the shebang) to find the first "perl" executable in the list of $PATH, and exec that program by appending the current file name after the shebang. The first snippets runs /bin/sh, the second runs /usr/bin/env. Neither of them invokes "perl" directly, like this shebang you'll find in most perl programs:


This is because most UNIX-like systems (especially Linux) comes with perl installed at /usr/bin/perl. Some UNIX does not have perl by default, but can be easily installed latter on. Just that they might end up being at /usr/local/bin/perl. Usually perl programmers call them "system perl".

A developer might install their own version of perl to anywhere like /opt/perl5.14.2/bin/perl or /opt/local/bin/perl or any other fancy paths. An put the bin path to the beginning of $PATH environment variable so that when they run perl programs as perl, it'll invoke their own version of perl, not the system perl. There is no special names for such perl installation, let's call them as "env perl" in the article.

perlbrew rely on this mechanism to be functional properly. Whenever you say perlbrew use 5.14.2, perlbrew locates the path of 5.14.2 perl binary, and modify $PATH to point to there. Running perl programs with env perl requires you type "perl" in the beginning of the command. Relying shebang lines in the code does not.

The shebang schema is like Dokodemo Door, a convention that brings huge convenience to easily specify an compiler + runner of the code. Without this design, scripting/dynamic language authors might just create their own conventions.

However, it also creates problems. A cross-platform perl program might not be set be executable because the shebang line is valid on the author's platform, but not on the users' environment. A perl program authored on Mac can simply put #!/usr/bin/perl in the first line, but once that program gets copied to FreeBSD, perl can only be found at #!/usr/local/bin/perl. The user intend to run the program should modify the code a little bit in order to make it a valid executable. Which really invalids the statement that "perl programs are cross-platform."

Well, that sounds like picking bones from a egg. Developers will eventually learn that they can either symlink perl around, or have some fancy scripts that properly modify shebang in-place. True. However when a program does not run because of a invalid shebang line, it can be really frustrated for newbies

Sadly, there is really no perfect cross-platform shebang that works for all UNIX-like systems.

However, CPAN distributions built with ExtUtil::MakeMaker solves this issue in a very good way. When perl programs are distributed / installed as a CPAN distribution, its shebang line are rewritten to be the same perl that used to invoke cpan client itself. Or, if you download+extract a CPAN distribution on your own and run perl Makefile.PL to do manual installation, it will be your env perl.

Since shebang is hard to be cross-platfrom, it is also not trivial to decide what to put on the perlbrew executable shebang. You'll find that it currently contains #!/usr/bin/perl. However, the true is, when users runs this installer:

curl -kL | bash

The downloaded perlbrew will be modified to use your system perl shebang. To be more specific, it is the pre-perlbrew env perl shebang. That means the first perl in $PATH that is not under $PERLRREW_ROOT.

You might remember that it used to be just #!/usr/bin/env perl. But that has at least 2 drawbacks:

  1. CentOS Linux comes without /usr/bin/env, but only /bin/env (well, the same for many other UNIXs)
  2. perlbrew use perl-5.6.0 will strangle perlbrew executable itself, because perlbrew requires 5.8.8.

Therefore, it is the best to let perlbrew itself fixed to use system perl shebang. You may leave your own program to use the env shebang so that it can be invoked with env perl, which is managed with perlbrew. Optionally, you might want to hard-coded a perlbrew perl shebang in your code for some use cases.

Shebang is cool. It even feels like a cool word to be spoken. But having to manage shebang in programs really sucks. shrug