# A change of env

Two years after resolving to switch to Python 3, I’ve done it. I believe it will stick because I’ve changed my defaults.

Pretty much since I first started using Anaconda, I’ve had a Python 3 environment installed alongside Python 2, but Python 2 was the default. Still is, but now I’ve fiddled a bit with my .bashrc to change the environment whenever I launch a new Terminal window. Here are the important lines:

# Anaconda Python
export PATH=$HOME/anaconda/bin:$PATH
source activate py3


The export line has always been there, and it’s still necessary to make all the Anaconda commands available. The source line is the new addition. It activates the Python 3 environment, which I called py3 back when I first installed it. So here’s how things work now from the shell:

• jupyter console starts a Jupyter session running Python 3.
• Scripts with a shebang line of #!/usr/bin/env python use the Python 3 at ~/anaconda/envs/py3/bin/python.
• Scripts with a shebang line of #!/usr/bin/env python2 use the Python 2 at ~/anaconda/bin/python2.
• Scripts with a shebang line of #!/usr/bin/env python3 use the Python 3 at ~/anaconda/envs/py3/bin/python3. Because of soft links in the Anaconda setup, this is the same Python 3 that #!/usr/bin/env python uses.

When I first did this, I found that Anaconda had changed my bash prompt, adding a (py3) to the front of it to let me know which environment I’m working in. I hated that, so I ran

conda config --set changeps1 False


to get rid of it. This added a line to my ~/.condarc,

changeps1: False


so the fix is persistent and I don’t need to add the conda config line to my .bashrc.

With this change, all my scripts with shebang lines of #!/usr/bin/env python are likely to fail because they were written with the expectation of Python 2 as the active environment. No problem. I ran the following pipeline from within my ~/Dropbox/bin directory, where I keep all my scripts. Now all those scripts have a shebang line of #!/usr/bin/env python2.

grep -l '^#!/usr/bin/env python$' * 2>/dev/null | xargs -n 1 perl -i -pe 's/python/python2/ if$.==1'


The grep -l part gets a list of all the files with a #!/usr/bin/env python shebang line. The 2>/dev/null isn’t necessary, but it silences the error messages grep spits out when it encounters a directory instead of a regular file.

The list of files from grep is then fed to xargs, which sends them one at a time (that’s the -n 1 switch) to perl, which edits the file in place (-i), substituting python2 for python on the first line.

The -n 1 option to xargs is important because without it, xargs would feed the entire list of files to perl at once, and Perl’s \$. variable would be interpreted as the first line of a concatenation of all the files. Only the first file in the list would get the change. With the -n 1 option, the perl command is called many times, each time with only one file argument, and all the files get changed.1

I also have a bunch of scripts with a shebang line of #!/usr/bin/python, which use the Apple-supplied Python 2. These scripts won’t fail because they call the same version Python they were written for, so I don’t feel any need to change them.

1. If you’re wondering whether I knew to include the -n 1 switch when I ran the command the first time, wonder no more. Of course I didn’t.