ETOOBUSY 🚀 minimal blogging for the impatient
A cheap trick to manipulate PERL5LIB
TL;DR
Manipulating
PERL5LIB
for locally installed programs.
In time, I’ve become fond of not installing Perl widely but keeping them tight in some place where they can’t interfere with others. This led to Installing Perl Modules, where each project gets its own rendition of modules.
Sometimes, though, I’d just like to install some Perl application to
have it available in the shell. Which basically means I install it
somewhere below my home directory - like the perl5
sub-directory that
cpanm
defaults to.
This creates a problem, though, because it means that I have to ensure
that PERL5LIB
is properly set to load modules from where they were
installed (~/perl5/lib
and the like). I usually can’t just set it in
the shell (like in ~/.bashrc
) because working in different projects
will probably mean I’m going to customize it shell by shell.
This usually meant that I installed a wrapper in ~/bin
like this:
#!/bin/sh
#
# ~/bin/galook - a wrapper for ~/perl5/bin/galook
#
export PERL5LIB="$HOME/perl5/lib/perl5:$PERL5LIB"
export PATH="$HOME/perl5/bin:$PATH"
exec "$HOME/perl5/bin/galook" "$@"
While I’m at it, I also ensure that multiple executables in that directory can find each other.
After a few wrappers, I got obviously tired and wanted to solve the
issue once and for all, so I created this shell script at
~/perl5/bin/__target__
:
#!/bin/sh
target="$(basename "$0")"
bindir="$(dirname "$(readlink -f "$0")")"
rootdir="$(dirname "$bindir")"
export PATH="$bindir:$PATH"
export PERL5LIB="$rootdir/lib/perl5:$PERL5LIB"
exec "$bindir/$target" "$@"
Then, instead of one ad-hoc wrapper ~/bin/galook
I just create it as
a symbolic link towards __target__
above:
$ ls -l ~/bin/galook
lrwxrwxrwx 1 you you 23 Oct 8 00:26 /home/you/bin/galook -> ../perl5/bin/__target__
So… how is this solving my issue? Let’s take a closer look:
1 #!/bin/sh
2 target="$(basename "$0")"
3 bindir="$(dirname "$(readlink -f "$0")")"
4 rootdir="$(dirname "$bindir")"
5 export PATH="$bindir:$PATH"
6 export PERL5LIB="$rootdir/lib/perl5:$PERL5LIB"
7 exec "$bindir/$target" "$@"
Remember that we will call the function with just galook
, and it will
be resolved by the shell to be ~/bin/galook
, eventually calling the
above script in ~/perl5/bin/__target__
, i.e. a script in the same
directory as our real program ~/perl5/bin/galook
.
When the script is called, $0
is simply galook
. Line 2 makes sure to
get just this name, so even calling it with its “full” path
~/bin/galook
would just yield galook
. This is important because it’s
the name of the real program we are after, although in a different
directory (i.e. ~/perl5/bin
).
Line 2 aims at finding the directory where the real program lives. Note that there are two nested operations:
readlink -f "$0"
resolves the program we are calling into the real script position, i.e. it gives back~/perl5/bin/__target__
;- calling
dirname
on it gives back~/perl5/bin
, i.e. exactly the directory we are after.
True, we could have just hardwired it, but it’s much more general like this 🤓
Now this is exactly what we need to manipulate PATH
(line 5), but we
still have to do one extra step to find where the Perl modules have
been installed to manipulate PERL5LIB
.
For this reason, line 4 computes the root directory for our local
installations, by just getting the parent of $bindir
(line 4). This
ends up being ~/perl5
. As modules are installed in lib/perl5
inside
this directory, line 5 installs $rootdir/lib/perl5
as the initial
path, so that we are fine.
Do you think I’m complicating bread? Let me know!