Nix Shell Shebangs

Shebangs

patching script interpreter paths in .
./shebang.py: interpreter directive changed from "#! /usr/bin/env python3" to "/nix/store/7ahkv87jj59z90yal5dcrgagz58cqmz6-python3-3.11.6/bin/python3"
./shebang.sh: interpreter directive changed from "#! /bin/sh" to "/nix/store/lm10ywzflq9qfhr4fl0zqxrhiksf28ks-bash-5.2-p15/bin/sh"

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:

#!/nix/store/lm10ywzflq9qfhr4fl0zqxrhiksf28ks-bash-5.2-p15/bin/sh

echo "hello world!"

If we run this script, we get:

hello world!

Here’s an example which runs Python:

#!/nix/store/7ahkv87jj59z90yal5dcrgagz58cqmz6-python3-3.11.6/bin/python3

print("hello world!")

Which outputs:

hello world!

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

nix-shell

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 python3 --run python3 << EOF
print("Hello world!")
EOF

Running this script will invoke /bin/sh as an interpreter (thanks to the shebang), then it will use nix-shell to temporarily install the python3 package. With the python3 package available, it will run the python3 command, and pipe in the script print("Hello world") to its stdin. Once that Python script has finished, the python3 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 python3 -p python3

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: