ETOOBUSY 🚀 minimal blogging for the impatient
A cheap trick to manipulate PERL5LIB
TL;DR
Manipulating
PERL5LIBfor 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
dirnameon 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!