Nix Shell Shebangs


A "shebang", written as #!, is a special code at the start of a file, which is used to tell the OS how to execute that file. For example, the top line of this script tells the OS to run it using the /bin/sh program:

#! /bin/sh

echo "hello world!"

If we run this script, we get:

hello world!

Here's an example which runs Python:

#! /usr/bin/env python

print "hello world!"

Which outputs:

hello world!

Notice that in the second case we don't call the python command directly; instead we call the env command, which looks up the python command in our $PATH variable (in my case, python is /nix/store/zlhph6b9r195zwrixccbm932f2ad0llf-python-2.7.13/bin/python, which I'd rather not hard-code!).


Since the Nix package manager can install programs "non-destructively" (ie. without disrupting existing software), we can install 'disposable' software; ie. install foo, run it, then remove it, without any other software noticing. To do this, we use nix-shell.

We tell nix-shell what software to install by providing either:

Inside this environment we can do a few things:

For example, we might need to run a Python script, but we either:

If we do know that Nix is available, we can do something like the following:

#! /bin/sh
nix-shell -p python --run python << EOF
print "Hello world!"

Running this script will invoke /bin/sh as an interpreter (thanks to the shebang), then it will use nix-shell to temporarily install the python package. With the python package available, it will run the python command, and pipe in the script print "Hello world" to its stdin. Once that Python script has finished, the python interpreter will exit, nix-shell will exit, and the environment it created will be available for garbage collection.

Notice a few things about our throw-away Python environment:

nix-shell shebangs

There are two obvious flies in our ointment: firstly, nix-shell must be installed, which we can't really get around; secondly, our Python code was written as a string in a shell script.

As of Nix 1.9 we can solve this second issue, by writing a regular Python script but using nix-shell as our shebang:

#! /usr/bin/env nix-shell
#! nix-shell -i python -p python

print "Hello world!"

This shebang has a few parts:

By using nix-shell in our shebangs like this, we're going one step further than /usr/bin/env: