Shell Script – Choose Interpreter After Script Start

execinterpretershebangshellshell-script

Is there any way to dynamically choose the interpreter that's executing a script? I have a script that I'm running on two different systems, and the interpreter I want to use is located in different locations on the two systems. What I end up having to to is change the hashbang line every time I switch over. I would like to do something that is the logical equivalent of this (I realize that this exact construct is impossible):

if running on system A:
    #!/path/to/python/on/systemA
elif running on system B:
    #!/path/on/systemB

#Rest of script goes here

Or even better would be this, so that it tries to use the first interpreter, and if it doesn't find it uses the second:

try:
    #!/path/to/python/on/systemA
except: 
    #!path/on/systemB

#Rest of script goes here

Obviously, I can instead execute it as
/path/to/python/on/systemA myscript.py
or
/path/on/systemB myscript.py
depending on where I am, but I actually have a wrapper script that launches myscript.py, so I would like to specify the path to the python interpreter programmatically rather than by hand.

Best Answer

No, that won't work. The two characters #! absolutely needs to be the first two characters in the file (how would you specify what interpreted the if-statement anyway?). This constitutes the "magic number" that the exec() family of functions detects when they determine whether a file that they are about to execute is a script (which needs an interpreter) or a binary file (which doesn't).

The format of the shebang line is quite strict. It needs to have an absolute path to an interpreter and at most one argument to it.

What you can do is to use env:

#!/usr/bin/env interpreter

Now, the path to env is usually /usr/bin/env, but technically that's no guarantee.

This allows you to adjust the PATH environment variable on each system so that interpreter (be it bash, python or perl or whatever you have) is found.

A downside with this approach is that it will be impossible to portably pass an argument to the interpreter.

This means that

#!/usr/bin/env awk -f

and

#!/usr/bin/env sed -f

is unlikely to work on some systems.

Another obvious approach is to use GNU autotools (or some simpler templating system) to find the interpreter and place the correct path into the file in a ./configure step, which would be run upon installing the script on each system.

One could also resort to running the script with an explicit interpreter, but that's obviously what you're trying to avoid:

$ sed -f script.sed
Related Question