nix-script lets you write quick scripts in compiled languages, transparently compile and cache them, and pull in whatever dependencies you need from the Nix ecosystem.
This README is intended more as a reference, but I also wrote a blog post to explain what this is and why it exists.
You might have guessed this already, but you can install this package with nix:
nix-env -if https://github.com/BrianHicks/nix-script/archive/main.tar.gz
You probably should use niv or similar, though!
Once you do, you can control the version of nixpkgs you use (see "Controlling nixpkgs version" below.)
This project's CI also pushes Linux builds to nix-script.cachix.org automatically.
I push macOS builds by hand when I remember.
(But there is not a lot of code in this project; a full rebuild is not too painful!)
The normal nix-script invocation is controlled using shebang lines.
Starting your file with #!/usr/bin/env nix-script makes these options available:
| What? | Shebang line | Notes |
|---|---|---|
| Compile the script to a binary | #!build |
The command specified here must read from SCRIPT_FILE and write to OUT_FILE |
| Specify build-time dependencies | #!buildInputs |
This should be a space-separated list of Nix expressions. For example, you can get ghc by specifying haskellPackages.ghc |
| Specify runtime dependencies | #!runtimeInputs |
This should be a space-separated list of Nix expressions. |
| Use an alternative interpreter | #!interpreter |
Useful for running non-compiled languages. The interpreter specified will be used in a new shebang line on the first line of the compiled program. (For example, if you specify bash the line will be #!/usr/bin/env bash.) |
You can also control these options via environment variables in wrapper scripts (see the source of nix-script-haskell/nix-script-haskell.sh for an example.)
| Shebang line | Environment variable |
|---|---|
#!build |
BUILD_COMMAND |
#!buildInputs |
BUILD_INPUTS |
#!runtimeInputs |
RUNTIME_INPUTS |
#!interpreter |
INTERPRETER |
nix-script also lets your compiled script know where it came from by setting the SCRIPT_FILE environment variable to what you would have gotten in $0 if it was a shell script.
Building a new version for every change can get a little tiresome while developing.
If you want a quicker feedback loop, you can invoke nix-script and friends like nix-script --shell path/to/script to drop into a development shell with your build- and runtime dependencies.
This won't run your build command, but it will let you run it yourself, play around in repls, etc.
If you are making a wrapper script, you may find the SHELL_RUN environment variable useful: it allows you to specify what command to run in the shell.
If your language ecosystem has some common watcher script, it might be nice to add a special mode to your wrapper for it!
(For example, nix-script-haskell has a --ghcid flag for this purpose.
See the source for how it's set up!)
nix-script-bash exists to let you specify exact versions of your dependencies via Nix.
For example:
#!/usr/bin/env nix-script-bash
#!runtimeInputs jq
jq --helpThis is quicker than using nix-shell shebangs because the runtime environment calculation will be cached.
nix-script-haskell is a convenience wrapper for Haskell scripts.
In addition to the regular nix-script options, this lets you specify #!haskellPackages, which should be a space-separated list of Haskell packages (you can get a list of available names by running nix-env -qaPA nixpkgs.haskellPackages.)
For example:
#!/usr/bin/env nix-script-haskell
#!haskellPackages text
{-# LANGUAGE OverloadedStrings #-}
import Data.Text.IO
main :: IO ()
main = Data.Text.IO.putStrLn "Hello, World!"Unlike other shebang options, #!haskellPackages does not have an equivalent setting in the environment.
You can get quick compilation feedback by running nix-script-haskell --ghcid path/to/your/script.hs.
By default, nix-script will use the version of nixpkgs we use to build scripts it's installed with.
If you want to change this, specify pinnedPkgs when installing nix-script.
For example, if you use niv that might look like:
let
sources = import ./nix/sources.nix { };
pkgs = import sources.nixpkgs { };
in pkgs.mkShell {
buildInputs =
[ (pkgs.callPackage sources.nix-script { pinnedPkgs = sources.nixpkgs; }) ];
}The package set is included in the cache key calculations, so if you change your package set your scripts will automatically be rebuilt the next time you run them.
You can check which package set the runner will build from by examining the source of the nix-script wrapper installed on your PATH.
I want my open-source work to support projects addressing the climate crisis (for example, projects in clean energy, public transit, reforestation, or sustainable agriculture.) If you are working on such a project, and find a bug or missing feature in any of my libraries, please let me know and I will treat your issue as high priority. I'd also be happy to support such projects in other ways, just ask!
nix-script is licensed under the BSD 3-Clause license, located at LICENSE.