bash scripting python – How to Have Multiple Possibilities in a Script’s Shebang Line

bashpythonscripting

I'm in a bit of an interesting situation where I have a Python script that can theoretically be run by a variety of users with a variety of environments (and PATHs) and on a variety of Linux systems. I want this script to be executable on as many of these as possible without artificial restrictions. Here are some known setups:

  • Python 2.6 is the system Python version, so python, python2, and python2.6 all exist in /usr/bin (and are equivalent).
  • Python 2.6 is the system Python version, as above, but Python 2.7 is installed alongside it as python2.7.
  • Python 2.4 is the system Python version, which my script does not support. In /usr/bin we have python, python2, and python2.4 which are equivalent, and python2.5, which the script supports.

I want to run the same executable python script on all three of these. It would be nice if it tried to use /usr/bin/python2.7 first, if it exists, then fall back to /usr/bin/python2.6, then fall back to /usr/bin/python2.5, then simply error out if none of those were present. I'm not too hung up on it using the most recent 2.x possible, though, as long as it's able to find one of the correct interpreters if present.

My first inclination was to change the shebang line from:

#!/usr/bin/python

to

#!/usr/bin/python2.[5-7]

since this works fine in bash. But running the script gives:

/usr/bin/python2.[5-7]: bad interpreter: No such file or directory

Okay, so I try the following, which also works in bash:

#!/bin/bash -c /usr/bin/python2.[5-7]

But again, this fails with:

/bin/bash: - : invalid option

Okay, obviously I could just write a separate shell script that finds the correct interpreter and runs the python script using whatever interpreter it found. I'd just find it a hassle to distribute two files where one should suffice as long as it's run with the most up-to-date python 2 interpreter installed. Asking people to invoke the interpreter explicitly (e.g., $ python2.5 script.py) is not an option. Relying on the user's PATH being set up a certain way is also not an option.

Edit:

Version checking within the Python script is not going to work since I'm using the "with" statement which exists as of Python 2.6 (and can be used in 2.5 with from __future__ import with_statement). This causes the script to fail immediately with a user-unfriendly SyntaxError, and prevents me from ever having an opportunity to check the version first and emit an appropriate error.

Example: (try this with a Python interpreter less than 2.6)

#!/usr/bin/env python

import sys

print "You'll never see this!"
sys.exit()

with open('/dev/null', 'w') as out:
    out.write('something')

Best Answer

I'm not expert, but I believe that you shouldn't specify exact python version to use and leave that choice to system/user.

Also you should use that instead of hardcoding path to python in script:

#!/usr/bin/env python

or

#!/usr/bin/env python3 (or python2)

It is recommended by Python doc in all versions:

A good choice is usually

#!/usr/bin/env python

which searches for the Python interpreter in the whole PATH. However, some Unices may not have the env command, so you may need to hardcode /usr/bin/python as the interpreter path.

In various distributions Python may be installed in different places, so env will search for it in PATH. It should be available in all major Linux distributions and from what I see in FreeBSD.

Script should be executed with that version of Python which is in your PATH and which is chosen by your distribution*.

If your script is compatible with all version of Python except 2.4 you should just check inside of it if it is run in Python 2.4 and print some info and exit.

More to read

  • Here you can find examples in what places Python might be installed in different systems.
  • Here you can find some advantages and disadvantages for using env.
  • Here you can find examples of PATH manipulation and different results.

Footnote

*In Gentoo there is tool called eselect. Using it you may set default versions of different applications (including Python) as default:

$ eselect python list
Available Python interpreters:
  [1]   python2.6
  [2]   python2.7 *
  [3]   python3.2
$ sudo eselect python set 1
$ eselect python list
Available Python interpreters:
  [1]   python2.6 *
  [2]   python2.7
  [3]   python3.2
Related Question