In a previous Nix post I mentioned how you can run once-off commands with Nix without polluting your system environment.

In this post, I want to demonstrate how you can create self-contained and reproducible scripts using the Nix shebang.

Simple cowsay script

Using the interactive nix-shell

To start, imagine a scenario where you want a cow to “moo” using cowsay.

The bash script moo.sh would contain.

#!/usr/bin/env bash

cowsay "moo"

To obtain the cowsay binary without polluting your system environment you would first generate a temporary environment.

nix-shell -p cowsay

And then execute the script.

./moo.sh

Using the nix-shell shebang

A cleaner, more contained, approach would be to use the nix-shell shebang.

The moo.sh script would then look like.

#!/usr/bin/env nix-shell
#!nix-shell -i bash -p cowsay

cowsay "moo"

The first line should always be #!/usr/bin/env nix-shell.

The second line describes the script language (in this case bash) and the dependencies (cowsay).

Executing the script is then just as simple as executing any executable.

./moo.sh

Using “pure” environments

The above example doesn’t strictly check if cowsay was declared in the dependencies. To enforce this check, i.e. all dependencies used in the script HAS to be declared on line 2, you need to use the --pure arg.

#!/usr/bin/env nix-shell
#!nix-shell --pure -i bash -p cowsay

cowsay "moo"

A real world example using my dot binary

I was able to simplify my dotfiles by replacing my shell.nix with a shebang declaration. See the diff.

Conclusion

The Nix shebang provides a cleaner, self-contained and reproducible (if you use the --pure arg) method of writing scripts.