Environment Variables in Shebangs

Hello,

I have a bunch of legacy CGIs, each supposed to work with a certain version of python (2.2, 2.3, 2.4, etc.). Configuring the shebangs of those is a cumbersome task and backwards compatibility is not always maintained in python releases, so simply putting python2.5 in the shebang of those scripts will not do.

I could go through those files one by one and add to the shebang the path to the appropriate version of the python interpreter. However, this will quickly become a nightmare once you deploy those to machine with different paths for different python versions.

So the idea I'm trying to implement is to get the shebang to launch the appropriate interpreter using an environment variable. So, the shebang itself would look something along this line:

In the script:
#!$PYTHON_24

And in apache's config, I would set up the environment variable appropriately:

SetEnv PYTHON_24 /usr/local/python2.4/bin/python

The expected action is that the shell resolves the environment variable and executes the interpreter its pointing to. But this doesn't work.

Does anyone know of a way to achieve what I want?

Much thanks to you.
Neked

Alas the operating system "exec()" which does the program loading does not expand environment variables, however you could put a link in there and then point the link at the appropriate version of python, or make the link point to a stub script which then invokes python, something like....

#!/usr/local/redirect/python_24

Hi.

I have an alternate suggestion. The problem you cite has been one of the thorns in the side of portability for a long time.

The perl folks early on published a script that would reset the shebang line to the local path.

Here's an example of fixing a python script that has only "python2.3" has the path:

#!/usr/bin/env sh

# @(#) s1       Demonstrate fixing the shebang line.

set -o nounset
echo

debug=":"
debug="echo"

## Use local command version for the commands in this demonstration.

echo "(Versions displayed with local utility \"version\")"
version >/dev/null 2>&1 && version bash fixin my-nl

# Stage erroneous script.
cp py0 py1

echo

echo " Original python script:"
my-nl py1

echo
echo " Attempt to run will fail:"
./py1

echo
echo " Results of fixin running on py1:"
fixin py1
my-nl py1
echo
./py1

exit 0

Producing:

% ./s1

(Versions displayed with local utility "version")
GNU bash 2.05b.0
fixin (local) 293
my-nl (local) 293

 Original python script:

==> py1 <==

  1 #!python2.3
  2
  3 # @(#) py1  Demonstrate python feature.
  4
  5 print ' Hello, world from python.'

 Attempt to run will fail:
./s1: ./py1: python2.3: bad interpreter: No such file or directory

 Results of fixin running on py1:
Changing py1 to /usr/bin/python2.3

==> py1 <==

  1 #!/usr/bin/python2.3
  2
  3 # @(#) py1  Demonstrate python feature.
  4
  5 print ' Hello, world from python.'

 Hello, world from python.

cheers, drl

This could work, but I suspect it won't make deployment any easier. The amount of work needed to create the symbolic links is the same as the amount needed to write and run a sed script to substitute all shebangs with the correct way. Unfortunately, this seems to be as easy as it can get in this case.

Thanks drl for the idea. It is similar to what I am doing now, which is running sed scripts to substitute interpreters in shebangs.

It is really strange that such an issue has lingered for a long time without a solution. I can imagine a couple of things that could solve this:

  • Changes to the exec() system function that porter referred to, such that it can handle environment variables. This might be too hard.

  • Create a program that takes the name of an environment variable and runs the executable that the environment variables points to. So if the program is called envexec, the shebang would look like this:

#!/usr/bin/envexec PYTHON_24

I imagine that one of the requirements of such a program is that it doesn't create a sub-shell on which to execute that command, but rather execute the interpreter in the same shell the system would execute it under if it was normally configured in the shebang.

Does anyone know of any programs around that do that? Or, for that matter, an easy way to implement such a program?

Hi.

I may be missing the point here. There are two ways that I know of to obtain portability for the shebang.

One is to run something like fixin, which searches the filesystem for the executables -- python2.3, perl, etc. -- and places that path in the shebang line.

The other is to use env like this:

#!/usr/bin/env python2.3

which seems to work provided the executable is named (in this case) "python2.3". This is what I've been doing lately. In the situations where env doesn't work on a new platform to which I am going, I fall back to fixin to make the changes to the shebang line "permanent" on the new platform.

The env solution has a slight security risk, bash Cookbook, p 321 ff.

I think Debian has some other table of alternatives / equivalents, but I don't know any details about it ... cheers, drl

I don't like relying on env for two reasons:

  • Env searches the path for python2.4 and runs the first match. This is not suitable for me, since not only do we have different versions of the interpreter but also interpreters such as stackless and activestate python. I'll need a utility like env, that searches the environment variables instead of searching the PATH.
  • As you mentioned there is a security risk to it.