Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Can I write a transparent shell wrapper to start the correct build of Perl?

by thparkth (Beadle)
on Feb 05, 2008 at 16:01 UTC ( #666330=perlquestion: print w/ replies, xml ) Need Help??
thparkth has asked for the wisdom of the Perl Monks concerning the following question:

This is really a shell question, not a Perl question, but for reasons that are hopefully obvious, it can't be done in Perl.

In our NFS environment we have various different platforms (Linuxes and AIX) sharing a common filesystem. It's useful to have central builds of certain programs - Perl being a good example, because it lets us have centrally installed modules too.

So let's say that we have

/nfs/perl/5.10/centos4/bin/perl

and

/nfs/perl/5.10/centos5/bin/perl

(The user environment is automatically set up so that the correct perl for their host is in the path.)

What I want to do is create a

/nfs/perl/5.10/bin/perl

which checks the platform of the current host, and starts the correct perl interpreter. I want it to work in a hash-bang line, and support command line parameters etc. Basically it should be invisible to the user that they aren't hitting the actual Perl binary directly.

I thought I had this working, with this script:

#!/bin/sh . /etc/host.conf exec /nfs/perl/5.10/${HOST_PLATFORM}/bin/perl "$@"

In fact, this seems to work on the majority of machines. But there are some machines where it doesn't work at all when used in a hash-bang line. Instead of starting perl and running the script with that, it tries to run the perl script through /bin/sh, with the entertaining but unproductive results that you would probably expect.

I can't see any pattern for which hosts work and which don't, though many have custom kernels which might be a factor.

Does anyone know of an alternative way to write this wrapper script which might be more reliable?

Comment on Can I write a transparent shell wrapper to start the correct build of Perl?
Download Code
Re: Can I write a transparent shell wrapper to start the correct build of Perl?
by Tanktalus (Canon) on Feb 05, 2008 at 16:21 UTC

    What if you go the other way? At the top of your perl scripts put this:

    #!/bin/sh eval '. /etc/host.conf; exec /nfs/perl/5.10/${HOST_PLATFORM}/bin/perl +-S $0 ${1+"$@"}' if 0;
    Admittedly, it's not as pretty as having all that ugliness hidden away in a separate, single location, but other than that, it may actually work.

      Thanks, yes, that's plan B. If there have to be crude shell gubbins going on I would ideally wish that they were invisible to the Perl programmer, if you know what I mean.
Re: Can I write a transparent shell wrapper to start the correct build of Perl?
by almut (Canon) on Feb 05, 2008 at 18:25 UTC

    Not sure I entirely understand your specific requirements, but why not create a symlink in the local filesystem (I'm assuming there is a local filesystem...) which points to the respective NFS location, e.g.

    ln -s /nfs/perl/5.10/centos5/bin/perl /usr/local/bin/perl

    for a centos5 machine. This is a one-time administration thing, and your shebang line in the Perl script would then simply be #!/usr/local/bin/perl.

      Symlinks don't work on the shebang line, IME.

        Why not, what's the problem? I've been using them like this for ages without any issues I could remember.  Resolution of symlinks is a rather low-level kernel feature, which should be handled transparently by any modern version of Unix.

        PS: anyone interested in how it's done in Linux may want to study the function load_script in linux/fs/binfmt_script.c, which calls open_exec (linux/fs/exec.c), which in turn eventually branches (via path_lookup_open) into the generic path lookup and symlink resolution code implemented in linux/fs/namei.c.

Re: Can I write a transparent shell wrapper to start the correct build of Perl?
by dwm042 (Priest) on Feb 05, 2008 at 18:52 UTC
    The first thing that comes to mind is that the only way you get that thing running with shell is if the perl in the exec line "doesn't exist."

    The second thing that comes to mind is how does this script behave if /etc/host.conf is empty. Shouldn't you define HOST_PLATFORM with some kind of sane default value before you source /etc/host.conf? Could the script figure it out for itself, with 'uname -a' perhaps?

    Another issue is that using variables in all capital letters in a shell script guarantees name collisions with the OS environment variables. Some other capitalization convention for those kinds of variables is preferable.

      The second thing that comes to mind is how does this script behave if /etc/host.conf is empty.

      It's not. If it is, the machine has bigger problems than perl not working.

      Shouldn't you define HOST_PLATFORM with some kind of sane default value before you source /etc/host.conf?

      There is no sane default value.

      Could the script figure it out for itself, with 'uname -a' perhaps?

      Not trivially. But it doesn't matter, as the host.conf file is reliable.

      Another issue is that using variables in all capital letters in a shell script guarantees name collisions with the OS environment variables.

      That's not the actual variable name. None of the paths or filenames I gave are real. Our platforms aren't really called centos4 and centos5. The main reason for this is that the real values all contain the company name, and I'd rather not get fired ;)

Re: Can I write a transparent shell wrapper to start the correct build of Perl?
by Porculus (Hermit) on Feb 05, 2008 at 19:52 UTC
    If "the user environment is automatically set up so that the correct perl for their host is in the path", why don't you just use #!/usr/bin/env perl...?

      There's a potential subtle problem with using env, which is that you typically can't pass arguments to the perl binary this way, like for example

      #!/usr/bin/env perl -w

      On my Debian Linux this produces

      $ ./666330.pl /usr/bin/env: perl -w: No such file or directory

      This is not a problem with env (running /usr/bin/env perl -w 666330.pl from the command line would work fine...), but rather with many Unix kernels, which treat everything after the interpreter specified on the shebang line (here /usr/bin/env) as a single argument, i.e. it would pass "perl -w" to /usr/bin/env as the program name to execute... which of course doesn't exist. This can easily be seen with strace:

      $ strace -e trace=execve ./666330.pl execve("./666330.pl", ["./666330.pl"], [/* 24 vars */]) = 0 execve("/home/almut/bin/perl -w", ["perl -w", "./666330.pl"], [/* 24 v +ars */]) = -1 ENOENT (No such file or directory) execve("/usr/local/bin/perl -w", ["perl -w", "./666330.pl"], [/* 24 va +rs */]) = -1 ENOENT (No such file or directory) execve("/usr/bin/perl -w", ["perl -w", "./666330.pl"], [/* 24 vars */] +) = -1 ENOENT (No such file or directory) execve("/bin/perl -w", ["perl -w", "./666330.pl"], [/* 24 vars */]) = +-1 ENOENT (No such file or directory) execve("/usr/bin/X11/perl -w", ["perl -w", "./666330.pl"], [/* 24 vars + */]) = -1 ENOENT (No such file or directory) /usr/bin/env: perl -w: No such file or directory
        I fixed builds that attempted to use the -w option when calling perl. The shell was treating it as a single command name rather than a command with a switch. The easy way to fix this would be to do something similar to: ln -s ./perl 'perl -w' This will create a symbolic link named 'perl -w' allowing the build to work, in my case. I was running into this with trying to compile the library pango and it was annoying me, because I couldn't figure out where env was calling for the file 'perl -w' to change it. The -w switch only enables some additional warnings, so not entirely necessary. Another option might be to create a script named 'perl -w' then have it call perl with the -w switch.
Re: Can I write a transparent shell wrapper to start the correct build of Perl?
by Anonymous Monk on Feb 05, 2008 at 21:38 UTC
    You need "eval" here, not "exec".
Re: Can I write a transparent shell wrapper to start the correct build of Perl?
by superfrink (Curate) on Feb 06, 2008 at 04:43 UTC
    We used to do this by having our shell login scripts (eg: .bashrc or maybe /etc/profile) check what platform and machine we were on and set aliases and paths as appropriate.

    Update: Oops, this will not work if you have scripts that use a #! line and the execute bit.
Re: Can I write a transparent shell wrapper to start the correct build of Perl?
by gam3 (Curate) on Feb 06, 2008 at 09:27 UTC
    Try it without the braces:
    #!/bin/sh . /etc/host.conf exec /nfs/perl/5.10/$HOST_PLATFORM/bin/perl "$@"
    -- gam3
    A picture is worth a thousand words, but takes 200K.
Re: Can I write a transparent shell wrapper to start the correct build of Perl?
by Tanktalus (Canon) on Oct 11, 2013 at 16:50 UTC

    I know this is an old thread, and I know I've already responded to it, but, since then, I've come up with and am using a completely different method for dealing with situations similar to this.

    I have a "dev_perl" script. Written in perl. And I put that into the #! line. (Well, I actually have it in the PATH and use #!/bin/env dev_perl, but close enough.)

    So, this dev_perl script is something like this:

    #!/usr/bin/perl my $os = $^O; # You may need something more complex - I do my $version = '5.10'; # I need more complex than this exec "/nfs/perl/$version/$os/bin/perl", @ARGV;
    In my case, I actually determine which workspace I'm in, version of our product, map that to the desired perl level, and look up where that level is from another file. And then I basically exec that version with all the original parameters. I also allow that if the command line has something that looks like /^5\.\d+\.\d+$/ as the first parameter, I shift that off and use it as the version instead of trying to detect the version. I could, instead, look for the command to run (which might not be the first parameter - I often use "dev_perl -d $tool @params" to debug stuff), and try to see if there is a "use 5.10.1" or some such in there to determine what level to run, I suppose, but I haven't found the need yet.

    Note how this script uses /usr/bin/perl - which should exist on all unix-like platforms. And the main reason for all this extra overhead? Because doing the complex stuff is far easier in perl than shell.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://666330]
Approved by ww
Front-paged by Tanktalus
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others perusing the Monastery: (7)
As of 2014-10-20 04:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (72 votes), past polls