Questions:
- What does the kernel do if you stick a shell-script into the shebang line?
- How does the Kernel know which interpreter to launch?
Explanation:
I recently wanted to write a wrapper around /usr/bin/env because my CGI Environment does not allow me to set the PATH variable, except globally (which of course sucks!).
So I thought, ‘OK. Let’s set PREPENDPATH and set PATH in a wrapper around env.’. The resulting script (here called env.1) looked like this:
#!/bin/bash /usr/bin/env PATH=$PREPENDPATH:$PATH $*
which looks like it should work. I checked how they both react, after setting PREPENDPATH:
$ which /usr/bin/env python /usr/bin/env /usr/bin/python $ which /usr/bin/env.1 python /usr/bin/env /home/pi/prepend/bin/python
Look absolutely perfect! So far, so good. But look what happens to ‘Hello World!’.
# Shebang is #!/usr/bin/env python $ test-env.py Hello World! # Shebang is #!/usr/bin/env.1 python $ test-env.1.py Warning: unknown mime-type for 'Hello World!' -- using 'application/*' Error: no such file 'Hello World!'
I guess I am missing something pretty fundamental about UNIX.
I’m pretty lost, even after looking at the source code of the original env. It sets the environment and launches the program (or so it seems to me…).
First of all, you should very seldom use
$*and you should almost always use'$@'instead. There are a number of questions here on SO which explain the ins and outs of why.Second – the
envcommand has two main uses. One is to print the current environment; the other is to completely control the environment of a command when it is run. The third use, which you are demonstrating, is to modify the environment, but frankly there’s no need for that – the shells are quite capable of handling that for you.Mode 1:
Mode 2:
This version cancels all inherited environment variables and runs
commandwith precisely the environment set by the ENVVAR=value options.The third mode – amending the environment – is less important because you can do that fine with regular (civilized) shells. (That means ‘not C shell’ – again, there are other questions on SO with answers that explain that.) For example, you could perfectly well do:
This insists that
$PREPENDPATHis set to a non-empty string in the environment, and then prepends it to$PATH, and exports the new PATH setting. Then, using that new PATH, it executes thepythonprogram with the relevant arguments. Theexecreplaces the shell script withpython. Note that this is quite different from:Superficially, this is the same. However, this will execute the
pythonfound on the pre-existing PATH, albeit with the new value of PATH in the process’s environment. So, in the example, you’d end up executing Python from/usr/binand not the one from/home/pi/prepend/bin.In your situation, I would probably not use
envand would just use an appropriate variant of the script with the explicit export.The
envcommand is unusual because it does not recognize the double-dash to separate options from the rest of the command. This is in part because it does not take many options, and in part because it is not clear whether the ENVVAR=value options should come before or after the double dash.I actually have a series of scripts for running (different versions of) a database server. These scripts really use
env(and a bunch of home-grown programs) to control the environment of the server: