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.