Press "Enter" to skip to content

Techy: Changing Python Shebang path from absolute to env

I’m just working in some Python code, and quite a lot of it has the “shebang” line for the Python interpreter set as an absolute path (such as /usr/bin/python) which won’t work for me as I need the scripts to use whichever version of Python I have configured in my “environment”: and the ideal way to do this is to call is using /usr/bin/env python – so how do I change these…

The shebang usually the first line of Linux scripts) set to:

#! /usr/bin/python

which is all well and good, apart from the fact I’m trying (because this is Python 2 code) to run it under “virtualenv” – which was easy to install as:

pip installĀ --upgrade pippip install virtualenv
virtualenv -p python2.7 ~/python2.7
source ~/python2.7/bin/activate

However, that means all those scripts (over 40 of them!) wouldn’t work as they would be looking for the Python interpreter in the wrong place (especially since some of this scripts are called by a bash script which then calls a python script which calls bash which then sets its own paths which then calls python…).

So to fix it, I just went into to root of the project and ran:

grep -RPIl '! ?/usr/bin/python' | xargs sed -i -E 's@! ?/usr/bin/python(2(\.[0-9]+)?)?@!/usr/bin/env python@g'

which uses grep to:

  • -R: perform a recursive search
  • -P: use Perl regular expressions to search
  • -I (capital I): skip binary files
  • -l (lower case L): return only names of files
  • ! ?/usr/bin/python

and once it’s found the list of suitable files, pass it into “xargs” which takes that output – line by line – and passes it to “sed” which does the actual replacement:

  • -i (lower case I): Edit the files in place. If you want a dry run (i.e. no changes), omit this value. Alternatively, if you want to make backups use “-i.backup” to add “.backup” to the originals
  • -E : treat the expression as an extension regular expression

and the regular expression itself:

  • s : substitution – i.e. when it finds text matching the “first part”, replace it with the second.
  • @ : use the “@” symbol as the delimiters (usually “/” – but that clashes with the file paths)
  • With the first part of the expression being:
    • ! ?” : look for strings starting with an exclamation mark then a space – but the space character is optional (indicated by the question mark)
    • /usr/bin/python : look for the string “/usr/bin/python”…
    • (2(\.[0-9]+)?)? : followed by the optional text…
    • (2(\.[0-9]+)?)? : the number two…
    • (2(\.[0-9]+)?)? : followed by the optional text…
    • (2(\.[0-9]+)?)? : of a full stop/period (normally this is a “meta” character indicating “any single character” – but by adding the backslash before it, we “escape” it back to normal)…
    • (2(\.[0-9]+)?)? : followed by any numbers between 0 and 9…
    • (2(\.[0-9]+)?)? : zero or more times
  • @ : indicates we’re starting the second part of the expression (as it’s the delimiter again)
  • !/usr/bin/env python : replace the text found by the first expression with just the text “!/usr/bin/env python”
  • @ : indicates we’re starting the third part of the expression
  • g: global – repeat this replacement/substitution globally across the whole file

Now my files have shebang lines of:

#!/usr/bin/env python
#!/usr/bin/env python3

which is perfect!

Be First to Comment

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.