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?
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
|
#!/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. | [reply] [d/l] |
|
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.
| [reply] |
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.
| [reply] [d/l] [select] |
|
Symlinks don't work on the shebang line, IME.
| [reply] |
|
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.
| [reply] [d/l] [select] |
|
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...? | [reply] [d/l] |
|
#!/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
| [reply] [d/l] [select] |
|
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.
| [reply] |
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.
| [reply] |
|
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 ;)
| [reply] |
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. | [reply] [d/l] [select] |
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". | [reply] |
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.
| [reply] [d/l] |
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. | [reply] [d/l] [select] |
|
Thanks a ton for this post! I applied the same technique and was successful!. It made my day. :-)
| [reply] |
|
|