Writing robust bash scripts with Nix

I have a lot of custom Bash scripts I use - some to perform simple commands like git log with some additional arguments, to more complex ones that mount and unmount USB devices plugged into my laptop.

A lot of people will post their scripts online along with their dotfiles for others to read and take inspiration from.

Mine are in my nix config directory and each script is added as a custom package I can install.

Here's an example of a script written as a Nix package:

{ pkgs }:

pkgs.writeShellApplication {
name = "get-tags";

runtimeInputs = with pkgs; [ git ];

text = ''
if [[ "$#" -gt 0 ]]; then
git tag | grep "$*"
exit 0
fi

git tag
'';
}

It gets a filtered list of Git tags within a repository using the git tag and grep commands.

Nix automatically adds the shebang line and sets some default shell options, so those aren't included within the script text.

It also automatically runs shellcheck to ensure the script is correct.

Injecting Dependencies

This script depends on git. Without it, it would not run successfully.

Instead of assuming it is installed, runtimeInputs ensures any dependencies are present and the script can be executed, even if the dependencies aren't enabled globally.

Scripts can also rely on other scripts.

get-tags has a corresponding count-tags script that counts the returned tags:

pkgs.writeShellApplication {
name = "count-tags";

runtimeInputs = with pkgs; [
coreutils
get-tags
];

text = ''
get-tags "''${1:-}" | wc -l
'';
}

get-tags is declared as a dependency, as well as coreutils to add the wc command that counts the lines.

Here's the thing

Using this approach gives me more robust scripts that are checked and verified, and will always have the required dependencies.

And, because they are added as custom Nix packages in my configuration, I am able to decide which scripts to install on which computer, giving me the most flexibility.